Update Programme
This commit is contained in:
parent
6032da4967
commit
7298c8642d
7 changed files with 643 additions and 191 deletions
58
README.md
58
README.md
|
|
@ -32,8 +32,14 @@ Via le reseau filaire (wired modems):
|
||||||
parametre (Y_VOL, Y_MINI, ANGLE_MAX, DIST_SOL, offsets, joystick,
|
parametre (Y_VOL, Y_MINI, ANGLE_MAX, DIST_SOL, offsets, joystick,
|
||||||
moteur...) puis passer `verifie = true`. Un warning s'affiche tant
|
moteur...) puis passer `verifie = true`. Un warning s'affiche tant
|
||||||
que ce n'est pas fait.
|
que ce n'est pas fait.
|
||||||
3. Calibrations depuis le moniteur tactile, DANS CET ORDRE (les
|
3. Calibrations depuis l'onglet CALIB, en mode CALIBRAGE pour
|
||||||
modes non calibres sont verrouilles en rouge). Les calibrations
|
celles au sol, DANS CET ORDRE (les modes non calibres sont
|
||||||
|
verrouilles en rouge). Toutes sont RE-EXECUTABLES (recablage:
|
||||||
|
relancer la calibration remet ses affectations a zero et prend
|
||||||
|
effet sans relancer le programme). La calib rsc verifie la
|
||||||
|
coherence (retour de l'assiette a l'etat initial entre chaque
|
||||||
|
impulsion, detection des conflits deux RSC -> une meme helice)
|
||||||
|
et s'annule proprement en cas d'incoherence. Les calibrations
|
||||||
assistees affichent leurs consignes sur l'ecran de conduite et se
|
assistees affichent leurs consignes sur l'ecran de conduite et se
|
||||||
valident en touchant le moniteur:
|
valident en touchant le moniteur:
|
||||||
- `gimbal` (assistee, en premier): incliner le nez vers le bas
|
- `gimbal` (assistee, en premier): incliner le nez vers le bas
|
||||||
|
|
@ -60,14 +66,29 @@ Via le reseau filaire (wired modems):
|
||||||
VITESSE_RAMPE, ANGLE_MAX) INVALIDE la calibration au prochain
|
VITESSE_RAMPE, ANGLE_MAX) INVALIDE la calibration au prochain
|
||||||
demarrage (retour aux gains de la conf + alerte).
|
demarrage (retour aux gains de la conf + alerte).
|
||||||
|
|
||||||
## Modes (boutons du moniteur; verrouilles en rouge si une
|
## Interface (moniteur tactile)
|
||||||
calibration manque, la raison s'affiche au toucher)
|
|
||||||
|
Echelle de texte automatique (la plus grande qui loge le contenu).
|
||||||
|
Navigation par onglets:
|
||||||
|
- MODE : boutons de modes + contenu adapte au mode courant (voir
|
||||||
|
ci-dessous), art du quadricoptere (cercles des helices: blanc
|
||||||
|
rpm=0, rouge rpm max, jaune en changement, vert stable; rpm des
|
||||||
|
propulseurs sur les cotes)
|
||||||
|
- CALIB : boutons des calibrations (gris = a faire, jaune = en
|
||||||
|
cours, vert = faite), consignes et barre de progression, art avec
|
||||||
|
le RSC en cours en VIOLET
|
||||||
|
- JOURNAL : dernieres lignes du journal de vol
|
||||||
|
|
||||||
|
## Modes (verrouilles en rouge si une calibration manque, la raison
|
||||||
|
s'affiche au toucher)
|
||||||
|
|
||||||
- `off` : moteur coupe (bottom a 15), tous les RSC a 0
|
- `off` : moteur coupe (bottom a 15), tous les RSC a 0
|
||||||
- `inactif` : moteur ALLUME, tous les RSC a 0, commandes desactivees,
|
- `inactif` : moteur ALLUME, tous les RSC a 0, commandes desactivees.
|
||||||
pret pour les calibrations. Dans tous les modes sauf off, une
|
Dans tous les modes sauf off, une erreur s'affiche si le moteur ne
|
||||||
erreur s'affiche si le moteur ne tourne pas (stressometer a
|
tourne pas (stressometer a capacite nulle) alors qu'il le devrait
|
||||||
capacite nulle) alors qu'il le devrait
|
- `calibrage` : identique a inactif mais AUTORISE les calibrations
|
||||||
|
au sol (gimbal, joystick, typew, rsc); capteurs et pid se lancent
|
||||||
|
en mode vol
|
||||||
- `stationnement` : helices a pleine puissance inversee (plaquage),
|
- `stationnement` : helices a pleine puissance inversee (plaquage),
|
||||||
propulseurs a 0, commandes desactivees. Conditions d'entree: au sol
|
propulseurs a 0, commandes desactivees. Conditions d'entree: au sol
|
||||||
(optical <= DIST_SOL) ET horizontal (<= ANGLE_MAX). Sortie
|
(optical <= DIST_SOL) ET horizontal (<= ANGLE_MAX). Sortie
|
||||||
|
|
@ -80,10 +101,12 @@ calibration manque, la raison s'affiche au toucher)
|
||||||
- `atterrissage` : descente douce (VITESSE_ATTERRISSAGE) jusqu'au
|
- `atterrissage` : descente douce (VITESSE_ATTERRISSAGE) jusqu'au
|
||||||
sol, puis stationnement. Sous Y_MINI, la descente exige un sol
|
sol, puis stationnement. Sous Y_MINI, la descente exige un sol
|
||||||
POSABLE (optical: bloc detecte, ni eau ni lave): garde-fou ocean
|
POSABLE (optical: bloc detecte, ni eau ni lave): garde-fou ocean
|
||||||
- `auto` : au clavier du PC: `auto <x> <z>`. Decollage a Y_VOL,
|
- `auto` : cible a 2 coordonnees (`auto <x> <z>` au clavier, pave
|
||||||
ralliement en avancant (cap estime par GPS), atterrissage sur place
|
tactile, ou bouton d'un point enregistre): decollage a Y_VOL,
|
||||||
- `position` : au clavier: `position <x> <y> <z>`. Rallie le point;
|
ralliement en avancant (cap estime par GPS), atterrissage sur
|
||||||
la descente est bloquee si le sol est detecte
|
place. Cible a 3 coordonnees (`auto <x> <y> <z>`): rallie le point
|
||||||
|
et s'y MAINTIENT a l'altitude y (bornee par le sol et Y_MINI).
|
||||||
|
Bouton ANNULER: retour en vol stabilise a la position courante
|
||||||
|
|
||||||
La consigne d'altitude se regle aux boutons +-1/+-10 du moniteur ou
|
La consigne d'altitude se regle aux boutons +-1/+-10 du moniteur ou
|
||||||
au TYPEWRITER: taper les chiffres (la saisie s'affiche sur l'ecran
|
au TYPEWRITER: taper les chiffres (la saisie s'affiche sur l'ecran
|
||||||
|
|
@ -109,8 +132,15 @@ Points nommes (clavier du PC, 10 max):
|
||||||
- Carburant: deux valeurs sur le 2e display link: POURCENTAGE de
|
- Carburant: deux valeurs sur le 2e display link: POURCENTAGE de
|
||||||
reserve (ligne 1) et AUTONOMIE en minutes (ligne 2), estimee par
|
reserve (ligne 1) et AUTONOMIE en minutes (ligne 2), estimee par
|
||||||
apprentissage glissant (EMA, CARBURANT.LISSAGE) de la
|
apprentissage glissant (EMA, CARBURANT.LISSAGE) de la
|
||||||
consommation; les ravitaillements sont exclus de l'apprentissage.
|
consommation; les ravitaillements et le mode off sont exclus de
|
||||||
Alerte sous SEUIL_ALERTE.
|
l'apprentissage. Alerte sous SEUIL_ALERTE.
|
||||||
|
- Moteur: pre-boost a pleine puissance (MOTEUR.BOOST secondes) a
|
||||||
|
chaque passage vers un regime exigeant (ex: inactif ->
|
||||||
|
stationnement) pour eviter l'overstress transitoire.
|
||||||
|
- Reactivite: le GPS (appel bloquant jusqu'a 2 s) est isole dans une
|
||||||
|
tache dediee alimentant un cache; l'interface et la navigation ne
|
||||||
|
bloquent jamais dessus. Messages longs de l'ecran de conduite:
|
||||||
|
defilement par segments de mots entiers (indicateur i/n).
|
||||||
|
|
||||||
## Fichiers
|
## Fichiers
|
||||||
|
|
||||||
|
|
|
||||||
244
drone.lua
244
drone.lua
|
|
@ -91,11 +91,17 @@ local mode = etat.mode or "off"
|
||||||
local consigneY = etat.consigneY or materiel.lireAltitude()
|
local consigneY = etat.consigneY or materiel.lireAltitude()
|
||||||
local cibleNav = nil
|
local cibleNav = nil
|
||||||
local phaseAuto = nil
|
local phaseAuto = nil
|
||||||
|
local distanceCible = nil
|
||||||
local stress = nil
|
local stress = nil
|
||||||
local carburantPct, autonomieMin = nil, nil
|
local carburantPct, autonomieMin = nil, nil
|
||||||
local saisieTypewriter = ""
|
local saisieTypewriter = ""
|
||||||
|
local saisieCible = ""
|
||||||
local enCalibration = false
|
local enCalibration = false
|
||||||
|
local calibEnCours = nil
|
||||||
|
local calibProgres, calibTexte = nil, nil
|
||||||
|
local rscViolet = nil
|
||||||
local alerteMoteurA = 0
|
local alerteMoteurA = 0
|
||||||
|
local boostMoteurFin = 0
|
||||||
|
|
||||||
local function sauverEtat()
|
local function sauverEtat()
|
||||||
etat.mode, etat.consigneY = mode, consigneY
|
etat.mode, etat.consigneY = mode, consigneY
|
||||||
|
|
@ -110,14 +116,14 @@ local function verrouDe(m)
|
||||||
local helicesOk = #materiel.helicesManquantes() == 0
|
local helicesOk = #materiel.helicesManquantes() == 0
|
||||||
local joystickOk = etat.joystick ~= nil
|
local joystickOk = etat.joystick ~= nil
|
||||||
|
|
||||||
if m == "off" or m == "inactif" then return nil end
|
if m == "off" or m == "inactif" or m == "calibrage" then return nil end
|
||||||
if not gimbalOk then return "calib gimbal requise" end
|
if not gimbalOk then return "calib gimbal requise" end
|
||||||
if m == "stationnement" then return nil end
|
if m == "stationnement" then return nil end
|
||||||
if not helicesOk then return "calib rsc requise" end
|
if not helicesOk then return "calib rsc requise" end
|
||||||
if (m == "vol" or m == "drone") and not joystickOk then
|
if (m == "vol" or m == "drone") and not joystickOk then
|
||||||
return "calib joystick requise"
|
return "calib joystick requise"
|
||||||
end
|
end
|
||||||
if (m == "auto" or m == "position") and not materiel.lirePosition() then
|
if m == "auto" and not materiel.lirePosition() then
|
||||||
return "pas de GPS"
|
return "pas de GPS"
|
||||||
end
|
end
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -125,8 +131,8 @@ end
|
||||||
|
|
||||||
local function verrous()
|
local function verrous()
|
||||||
local v = {}
|
local v = {}
|
||||||
for _, m in ipairs({ "off", "inactif", "stationnement", "vol",
|
for _, m in ipairs({ "off", "inactif", "calibrage", "stationnement",
|
||||||
"drone", "atterrissage", "auto", "position" }) do
|
"vol", "drone", "atterrissage", "auto" }) do
|
||||||
v[m] = verrouDe(m)
|
v[m] = verrouDe(m)
|
||||||
end
|
end
|
||||||
return v
|
return v
|
||||||
|
|
@ -151,6 +157,15 @@ local function changerMode(nouveau, raison)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- pre-boost du moteur avant les regimes exigeants (evite
|
||||||
|
-- l'overstress transitoire, ex: inactif -> stationnement)
|
||||||
|
local exigeants = { stationnement = true, vol = true, drone = true,
|
||||||
|
atterrissage = true, auto = true }
|
||||||
|
if exigeants[nouveau] then
|
||||||
|
boostMoteurFin = os.clock() + conf.MOTEUR.BOOST
|
||||||
|
materiel.reglerMoteur(15)
|
||||||
|
end
|
||||||
|
|
||||||
mode = nouveau
|
mode = nouveau
|
||||||
pilotage.raz()
|
pilotage.raz()
|
||||||
navigation.razCap()
|
navigation.razCap()
|
||||||
|
|
@ -161,7 +176,7 @@ local function changerMode(nouveau, raison)
|
||||||
phaseAuto = "decollage"
|
phaseAuto = "decollage"
|
||||||
local x, y, z = materiel.lirePosition()
|
local x, y, z = materiel.lirePosition()
|
||||||
etat.decollage = { x = x, y = y, z = z }
|
etat.decollage = { x = x, y = y, z = z }
|
||||||
consigneY = conf.Y_VOL
|
consigneY = cibleNav.y or conf.Y_VOL
|
||||||
elseif mode == "off" then
|
elseif mode == "off" then
|
||||||
pilotage.arreter()
|
pilotage.arreter()
|
||||||
end
|
end
|
||||||
|
|
@ -202,8 +217,8 @@ local function pasControle()
|
||||||
if mode == "off" then
|
if mode == "off" then
|
||||||
pilotage.arreter()
|
pilotage.arreter()
|
||||||
|
|
||||||
elseif mode == "inactif" then
|
elseif mode == "inactif" or mode == "calibrage" then
|
||||||
-- moteur allume, tout a zero, pret pour les calibrations
|
-- moteur allume, tout a zero; calibrage autorise les calibrations
|
||||||
materiel.toutArreter()
|
materiel.toutArreter()
|
||||||
|
|
||||||
elseif mode == "stationnement" then
|
elseif mode == "stationnement" then
|
||||||
|
|
@ -234,10 +249,22 @@ local function pasControle()
|
||||||
pilotage.reguler(consigneY, { tangage = 0, roulis = 0 }, 0, 0, DT)
|
pilotage.reguler(consigneY, { tangage = 0, roulis = 0 }, 0, 0, DT)
|
||||||
|
|
||||||
elseif mode == "auto" then
|
elseif mode == "auto" then
|
||||||
|
-- cible a 2 coordonnees (x, z): mission complete avec
|
||||||
|
-- atterrissage; cible a 3 (x, y, z): rallier et MAINTENIR a y
|
||||||
|
local yCroisiere = cibleNav.y or conf.Y_VOL
|
||||||
|
if cibleNav.y and cibleNav.y < materiel.lireAltitude() then
|
||||||
|
-- descente limitee par la presence d'un sol
|
||||||
|
if materiel.lireDistanceSol() <= conf.DIST_SOL + 0.5 then
|
||||||
|
yCroisiere = materiel.lireAltitude()
|
||||||
|
elseif cibleNav.y < conf.Y_MINI and not materiel.solPosable() then
|
||||||
|
yCroisiere = conf.Y_MINI
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if phaseAuto == "decollage" then
|
if phaseAuto == "decollage" then
|
||||||
local alt = pilotage.reguler(conf.Y_VOL,
|
local alt = pilotage.reguler(yCroisiere,
|
||||||
{ tangage = 0, roulis = 0 }, 0, 0, DT)
|
{ tangage = 0, roulis = 0 }, 0, 0, DT)
|
||||||
if math.abs(alt - conf.Y_VOL) <= 0.5 then
|
if math.abs(alt - yCroisiere) <= 0.5 then
|
||||||
phaseAuto = "croisiere"
|
phaseAuto = "croisiere"
|
||||||
journal.info("auto: croisiere vers la cible")
|
journal.info("auto: croisiere vers la cible")
|
||||||
end
|
end
|
||||||
|
|
@ -248,13 +275,25 @@ local function pasControle()
|
||||||
changerMode("vol", "GPS perdu")
|
changerMode("vol", "GPS perdu")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
pilotage.reguler(conf.Y_VOL, { tangage = 0, roulis = 0 },
|
distanceCible = distance
|
||||||
|
pilotage.reguler(yCroisiere, { tangage = 0, roulis = 0 },
|
||||||
avance, virage, DT)
|
avance, virage, DT)
|
||||||
if distance <= conf.NAV.SEUIL_ARRIVEE then
|
if distance <= conf.NAV.SEUIL_ARRIVEE then
|
||||||
phaseAuto = "atterrissage"
|
if cibleNav.y then
|
||||||
consigneY = materiel.lireAltitude()
|
phaseAuto = "maintien"
|
||||||
journal.info("auto: cible atteinte, atterrissage")
|
journal.info("auto: cible atteinte, maintien en position")
|
||||||
|
else
|
||||||
|
phaseAuto = "atterrissage"
|
||||||
|
consigneY = materiel.lireAltitude()
|
||||||
|
journal.info("auto: cible atteinte, atterrissage")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
elseif phaseAuto == "maintien" then
|
||||||
|
local avance, virage, distance = navigation.rallier(
|
||||||
|
cibleNav.x, cibleNav.z)
|
||||||
|
distanceCible = distance
|
||||||
|
pilotage.reguler(yCroisiere, { tangage = 0, roulis = 0 },
|
||||||
|
avance or 0, virage or 0, DT)
|
||||||
else
|
else
|
||||||
if materiel.auSol() then
|
if materiel.auSol() then
|
||||||
changerMode("stationnement", "mission terminee")
|
changerMode("stationnement", "mission terminee")
|
||||||
|
|
@ -263,24 +302,6 @@ local function pasControle()
|
||||||
consigneY = descendre()
|
consigneY = descendre()
|
||||||
pilotage.reguler(consigneY, { tangage = 0, roulis = 0 }, 0, 0, DT)
|
pilotage.reguler(consigneY, { tangage = 0, roulis = 0 }, 0, 0, DT)
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif mode == "position" then
|
|
||||||
local avance, virage = navigation.rallier(cibleNav.x, cibleNav.z)
|
|
||||||
if not avance then
|
|
||||||
changerMode("vol", "GPS perdu")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local cibleY = cibleNav.y
|
|
||||||
if cibleY < materiel.lireAltitude() then
|
|
||||||
-- descente limitee par la presence d'un sol
|
|
||||||
if materiel.lireDistanceSol() <= conf.DIST_SOL + 0.5 then
|
|
||||||
cibleY = materiel.lireAltitude()
|
|
||||||
elseif cibleY < conf.Y_MINI and not materiel.solPosable() then
|
|
||||||
cibleY = conf.Y_MINI
|
|
||||||
end
|
|
||||||
end
|
|
||||||
pilotage.reguler(cibleY, { tangage = 0, roulis = 0 },
|
|
||||||
avance, virage, DT)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -290,16 +311,32 @@ end
|
||||||
local ui = {
|
local ui = {
|
||||||
inviter = function(texte)
|
inviter = function(texte)
|
||||||
ihm.message(texte, 3600)
|
ihm.message(texte, 3600)
|
||||||
|
calibTexte = texte
|
||||||
pcall(ihm.rafraichirConduite,
|
pcall(ihm.rafraichirConduite,
|
||||||
{ mode = mode, consigneY = consigneY })
|
{ mode = mode, consigneY = consigneY })
|
||||||
journal.info("[calib] " .. texte)
|
journal.info("[calib] " .. texte)
|
||||||
end,
|
end,
|
||||||
|
progres = function(pct, texte)
|
||||||
|
calibProgres, calibTexte = pct, texte
|
||||||
|
end,
|
||||||
|
rscViolet = function(role) rscViolet = role end,
|
||||||
attendreToucher = ihm.attendreToucher,
|
attendreToucher = ihm.attendreToucher,
|
||||||
}
|
}
|
||||||
|
|
||||||
local function lancerCalibration(quoi)
|
local function lancerCalibration(quoi)
|
||||||
enCalibration = true
|
enCalibration = true
|
||||||
|
calibEnCours = quoi
|
||||||
|
calibProgres, calibTexte = 0, quoi
|
||||||
local ok, err = pcall(function()
|
local ok, err = pcall(function()
|
||||||
|
if quoi == "gimbal" or quoi == "joystick" or quoi == "typew"
|
||||||
|
or quoi == "rsc" then
|
||||||
|
if mode ~= "calibrage" then
|
||||||
|
journal.alerte("calib " .. quoi
|
||||||
|
.. ": passer en mode calibrage d'abord")
|
||||||
|
ihm.message("passer en mode calibrage")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
if quoi == "gimbal" then
|
if quoi == "gimbal" then
|
||||||
Calibration.gimbal(conf, etat, materiel, journal, Etat, ui)
|
Calibration.gimbal(conf, etat, materiel, journal, Etat, ui)
|
||||||
elseif quoi == "joystick" then
|
elseif quoi == "joystick" then
|
||||||
|
|
@ -307,17 +344,14 @@ local function lancerCalibration(quoi)
|
||||||
elseif quoi == "typew" then
|
elseif quoi == "typew" then
|
||||||
Calibration.typewriter(conf, etat, materiel, journal, Etat, ui)
|
Calibration.typewriter(conf, etat, materiel, journal, Etat, ui)
|
||||||
elseif quoi == "rsc" then
|
elseif quoi == "rsc" then
|
||||||
if mode ~= "inactif" then
|
Calibration.rsc(conf, etat, materiel, journal, Etat, ui)
|
||||||
journal.alerte("calib rsc: passer en mode inactif d'abord")
|
|
||||||
else
|
|
||||||
Calibration.rsc(conf, etat, materiel, journal, Etat, ui)
|
|
||||||
end
|
|
||||||
elseif quoi == "capteurs" or quoi == "pid" then
|
elseif quoi == "capteurs" or quoi == "pid" then
|
||||||
if mode ~= "vol" then
|
if mode ~= "vol" then
|
||||||
journal.alerte("calib " .. quoi .. ": passer en mode vol d'abord")
|
journal.alerte("calib " .. quoi .. ": passer en mode vol d'abord")
|
||||||
|
ihm.message("passer en mode vol")
|
||||||
elseif quoi == "pid" then
|
elseif quoi == "pid" then
|
||||||
Calibration.pid(conf, etat, materiel, pilotage, journal, Etat,
|
Calibration.pid(conf, etat, materiel, pilotage, journal, Etat,
|
||||||
empreinte)
|
empreinte, ui)
|
||||||
else
|
else
|
||||||
local function tenir(duree, angles, avance)
|
local function tenir(duree, angles, avance)
|
||||||
local cible = materiel.lireAltitude()
|
local cible = materiel.lireAltitude()
|
||||||
|
|
@ -346,9 +380,29 @@ local function lancerCalibration(quoi)
|
||||||
end)
|
end)
|
||||||
if not ok then journal.erreur("calibration: " .. tostring(err)) end
|
if not ok then journal.erreur("calibration: " .. tostring(err)) end
|
||||||
ihm.message("", 0)
|
ihm.message("", 0)
|
||||||
|
calibEnCours, rscViolet = nil, nil
|
||||||
enCalibration = false
|
enCalibration = false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- statuts des calibrations pour la page CALIB
|
||||||
|
local function calibStatuts()
|
||||||
|
local faits = {
|
||||||
|
gimbal = etat.gimbal ~= nil,
|
||||||
|
joystick = etat.joystick ~= nil,
|
||||||
|
typew = etat.typewriter ~= nil,
|
||||||
|
rsc = #materiel.helicesManquantes() == 0
|
||||||
|
and #materiel.propulseursManquants() == 0,
|
||||||
|
capteurs = #materiel.axesManquants() == 0,
|
||||||
|
pid = etat.pid ~= nil,
|
||||||
|
}
|
||||||
|
local statuts = {}
|
||||||
|
for nom, fait in pairs(faits) do
|
||||||
|
if calibEnCours == nom then statuts[nom] = "encours"
|
||||||
|
elseif fait then statuts[nom] = "fait" end
|
||||||
|
end
|
||||||
|
return statuts
|
||||||
|
end
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
--------------------------------------------------------------------
|
||||||
-- POINTS NOMMES / RETOUR DECOLLAGE
|
-- POINTS NOMMES / RETOUR DECOLLAGE
|
||||||
--------------------------------------------------------------------
|
--------------------------------------------------------------------
|
||||||
|
|
@ -408,6 +462,9 @@ local function tacheMoteur()
|
||||||
while true do
|
while true do
|
||||||
if mode == "off" then
|
if mode == "off" then
|
||||||
materiel.reglerMoteur(0)
|
materiel.reglerMoteur(0)
|
||||||
|
elseif os.clock() < boostMoteurFin then
|
||||||
|
materiel.reglerMoteur(15)
|
||||||
|
niveau = 15
|
||||||
else
|
else
|
||||||
stress = materiel.lireStress()
|
stress = materiel.lireStress()
|
||||||
if stress then
|
if stress then
|
||||||
|
|
@ -450,7 +507,7 @@ local function tacheCarburant()
|
||||||
carburantPct = quantite / capacite
|
carburantPct = quantite / capacite
|
||||||
local t = os.clock()
|
local t = os.clock()
|
||||||
|
|
||||||
if quantitePrec and t > tPrec then
|
if quantitePrec and t > tPrec and mode ~= "off" then
|
||||||
local conso = (quantitePrec - quantite) / (t - tPrec)
|
local conso = (quantitePrec - quantite) / (t - tPrec)
|
||||||
if conso >= 0 then -- conso negative = ravitaillement: ignore
|
if conso >= 0 then -- conso negative = ravitaillement: ignore
|
||||||
local a = conf.CARBURANT.LISSAGE
|
local a = conf.CARBURANT.LISSAGE
|
||||||
|
|
@ -525,39 +582,77 @@ local function tacheTypewriter()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- GPS: seule tache autorisee a faire l'appel bloquant (jusqu'a 2 s)
|
||||||
|
local function tachePosition()
|
||||||
|
while true do
|
||||||
|
if mode ~= "off" then
|
||||||
|
pcall(materiel.actualiserPosition)
|
||||||
|
end
|
||||||
|
sleep(3)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function contexteIhm()
|
||||||
|
local px, py, pz = materiel.lirePosition()
|
||||||
|
return {
|
||||||
|
mode = mode,
|
||||||
|
consigneY = consigneY,
|
||||||
|
stress = stress,
|
||||||
|
moteurOk = mode == "off" or materiel.moteurTourne(),
|
||||||
|
carburantPct = carburantPct,
|
||||||
|
minutes = autonomieMin,
|
||||||
|
position = px and { x = px, y = py, z = pz } or nil,
|
||||||
|
points = etat.points,
|
||||||
|
verrous = verrous(),
|
||||||
|
calibStatuts = calibStatuts(),
|
||||||
|
calibProgres = calibProgres,
|
||||||
|
calibTexte = calibTexte,
|
||||||
|
rscViolet = rscViolet,
|
||||||
|
cible = cibleNav and (cibleNav.y
|
||||||
|
and ("%d %d %d"):format(cibleNav.x, cibleNav.y, cibleNav.z)
|
||||||
|
or ("%d %d"):format(cibleNav.x, cibleNav.z)) or nil,
|
||||||
|
distanceCible = distanceCible,
|
||||||
|
saisieCible = saisieCible,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- lance la cible saisie: "x z" ou "x y z"
|
||||||
|
local function lancerCible(texte)
|
||||||
|
local nombres = {}
|
||||||
|
for n in texte:gmatch("%-?%d+") do table.insert(nombres, tonumber(n)) end
|
||||||
|
if #nombres == 2 then
|
||||||
|
cibleNav = { x = nombres[1], z = nombres[2] }
|
||||||
|
changerMode("auto", "cible x z")
|
||||||
|
elseif #nombres == 3 then
|
||||||
|
cibleNav = { x = nombres[1], y = nombres[2], z = nombres[3] }
|
||||||
|
changerMode("auto", "cible x y z")
|
||||||
|
else
|
||||||
|
ihm.message("format: x z ou x y z")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local function tacheIhm()
|
local function tacheIhm()
|
||||||
local prochainDessin = 0
|
local prochainDessin = 0
|
||||||
|
local redessiner = false
|
||||||
while true do
|
while true do
|
||||||
if os.clock() >= prochainDessin and not enCalibration then
|
if (os.clock() >= prochainDessin or redessiner) then
|
||||||
local px, py, pz = materiel.lirePosition()
|
redessiner = false
|
||||||
pcall(ihm.rafraichirConduite, {
|
pcall(ihm.rafraichirConduite, {
|
||||||
mode = mode, consigneY = consigneY, saisie = saisieTypewriter,
|
mode = mode, consigneY = consigneY, saisie = saisieTypewriter,
|
||||||
})
|
})
|
||||||
pcall(ihm.rafraichirMoniteur, {
|
pcall(ihm.rafraichirMoniteur, contexteIhm())
|
||||||
mode = mode,
|
prochainDessin = os.clock() + 1.0
|
||||||
consigneY = consigneY,
|
|
||||||
stress = stress,
|
|
||||||
moteurOk = mode == "off" or materiel.moteurTourne(),
|
|
||||||
gimbalOk = etat.gimbal ~= nil,
|
|
||||||
carburantPct = carburantPct,
|
|
||||||
minutes = autonomieMin,
|
|
||||||
position = px and { x = px, y = py, z = pz } or nil,
|
|
||||||
points = nbPoints(),
|
|
||||||
verrous = verrous(),
|
|
||||||
})
|
|
||||||
prochainDessin = os.clock() + 0.5
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local minuteur = os.startTimer(0.5)
|
local minuteur = os.startTimer(0.25)
|
||||||
local ev, a, b, c = os.pullEvent()
|
local ev, a, b, c = os.pullEvent()
|
||||||
if ev == "monitor_touch" and not enCalibration then
|
if ev == "monitor_touch" then
|
||||||
local action = ihm.traiterToucher(b, c)
|
local action = not enCalibration and ihm.traiterToucher(b, c)
|
||||||
if action then
|
if action then
|
||||||
|
redessiner = true
|
||||||
if action.type == "mode" then
|
if action.type == "mode" then
|
||||||
if action.verrou then
|
if action.verrou then
|
||||||
ihm.message(action.valeur .. ": " .. action.verrou)
|
ihm.message(action.valeur .. ": " .. action.verrou)
|
||||||
elseif action.valeur == "auto" or action.valeur == "position" then
|
|
||||||
ihm.message("cible au clavier: " .. action.valeur .. " x [y] z")
|
|
||||||
else
|
else
|
||||||
changerMode(action.valeur, "moniteur")
|
changerMode(action.valeur, "moniteur")
|
||||||
end
|
end
|
||||||
|
|
@ -566,6 +661,23 @@ local function tacheIhm()
|
||||||
sauverEtat()
|
sauverEtat()
|
||||||
elseif action.type == "calib" then
|
elseif action.type == "calib" then
|
||||||
lancerCalibration(action.valeur)
|
lancerCalibration(action.valeur)
|
||||||
|
elseif action.type == "ptgo" then
|
||||||
|
local p = etat.points[action.nom]
|
||||||
|
if p then
|
||||||
|
cibleNav = { x = p.x, z = p.z }
|
||||||
|
changerMode("auto", "pt " .. action.nom)
|
||||||
|
end
|
||||||
|
elseif action.type == "annuler" then
|
||||||
|
changerMode("vol", "annulation auto")
|
||||||
|
elseif action.type == "pave" then
|
||||||
|
if action.valeur == "eff" then
|
||||||
|
saisieCible = saisieCible:sub(1, -2)
|
||||||
|
elseif action.valeur == "go" then
|
||||||
|
lancerCible(saisieCible)
|
||||||
|
saisieCible = ""
|
||||||
|
else
|
||||||
|
saisieCible = (saisieCible .. action.valeur):sub(1, 18)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -579,13 +691,13 @@ local function tacheClavier()
|
||||||
local mots = {}
|
local mots = {}
|
||||||
for mot in ligne:gmatch("%S+") do table.insert(mots, mot) end
|
for mot in ligne:gmatch("%S+") do table.insert(mots, mot) end
|
||||||
if mots[1] == "auto" and tonumber(mots[2]) and tonumber(mots[3]) then
|
if mots[1] == "auto" and tonumber(mots[2]) and tonumber(mots[3]) then
|
||||||
cibleNav = { x = tonumber(mots[2]), z = tonumber(mots[3]) }
|
if tonumber(mots[4]) then
|
||||||
|
cibleNav = { x = tonumber(mots[2]), y = tonumber(mots[3]),
|
||||||
|
z = tonumber(mots[4]) }
|
||||||
|
else
|
||||||
|
cibleNav = { x = tonumber(mots[2]), z = tonumber(mots[3]) }
|
||||||
|
end
|
||||||
changerMode("auto", "clavier")
|
changerMode("auto", "clavier")
|
||||||
elseif mots[1] == "position" and tonumber(mots[2])
|
|
||||||
and tonumber(mots[3]) and tonumber(mots[4]) then
|
|
||||||
cibleNav = { x = tonumber(mots[2]), y = tonumber(mots[3]),
|
|
||||||
z = tonumber(mots[4]) }
|
|
||||||
changerMode("position", "clavier")
|
|
||||||
elseif mots[1] == "pt" then
|
elseif mots[1] == "pt" then
|
||||||
commandePoint(mots)
|
commandePoint(mots)
|
||||||
elseif mots[1] == "rtb" then
|
elseif mots[1] == "rtb" then
|
||||||
|
|
@ -596,7 +708,7 @@ local function tacheClavier()
|
||||||
print("pas de point de decollage enregistre")
|
print("pas de point de decollage enregistre")
|
||||||
end
|
end
|
||||||
elseif mots[1] then
|
elseif mots[1] then
|
||||||
print("auto <x> <z> | position <x> <y> <z> | pt ... | rtb")
|
print("auto <x> <z> | auto <x> <y> <z> | pt ... | rtb")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -607,4 +719,4 @@ if mode == "vol" or mode == "drone" then
|
||||||
consigneY = materiel.lireAltitude()
|
consigneY = materiel.lireAltitude()
|
||||||
end
|
end
|
||||||
parallel.waitForAny(tacheControle, tacheMoteur, tacheCarburant,
|
parallel.waitForAny(tacheControle, tacheMoteur, tacheCarburant,
|
||||||
tacheTypewriter, tacheIhm, tacheClavier)
|
tachePosition, tacheTypewriter, tacheIhm, tacheClavier)
|
||||||
|
|
|
||||||
|
|
@ -27,10 +27,12 @@ end
|
||||||
function Calibration.gimbal(conf, etat, materiel, journal, Etat, ui)
|
function Calibration.gimbal(conf, etat, materiel, journal, Etat, ui)
|
||||||
local seuil = conf.CALIB.IMPULSION_SEUIL
|
local seuil = conf.CALIB.IMPULSION_SEUIL
|
||||||
|
|
||||||
|
ui.progres(0, "mise a plat")
|
||||||
ui.inviter("Drone a plat, touchez l'ecran")
|
ui.inviter("Drone a plat, touchez l'ecran")
|
||||||
ui.attendreToucher()
|
ui.attendreToucher()
|
||||||
local x0, z0 = materiel.lireAnglesBruts()
|
local x0, z0 = materiel.lireAnglesBruts()
|
||||||
|
|
||||||
|
ui.progres(0.25, "nez bas")
|
||||||
ui.inviter("Inclinez NEZ vers le BAS, touchez")
|
ui.inviter("Inclinez NEZ vers le BAS, touchez")
|
||||||
ui.attendreToucher()
|
ui.attendreToucher()
|
||||||
local x1, z1 = materiel.lireAnglesBruts()
|
local x1, z1 = materiel.lireAnglesBruts()
|
||||||
|
|
@ -45,10 +47,12 @@ function Calibration.gimbal(conf, etat, materiel, journal, Etat, ui)
|
||||||
-- convention: nez bas => tangage normalise NEGATIF
|
-- convention: nez bas => tangage normalise NEGATIF
|
||||||
local signeTangage = (deltaT < 0) and 1 or -1
|
local signeTangage = (deltaT < 0) and 1 or -1
|
||||||
|
|
||||||
|
ui.progres(0.5, "retour a plat")
|
||||||
ui.inviter("Revenez a plat, touchez")
|
ui.inviter("Revenez a plat, touchez")
|
||||||
ui.attendreToucher()
|
ui.attendreToucher()
|
||||||
x0, z0 = materiel.lireAnglesBruts()
|
x0, z0 = materiel.lireAnglesBruts()
|
||||||
|
|
||||||
|
ui.progres(0.75, "penche a gauche")
|
||||||
ui.inviter("Penchez a GAUCHE, touchez")
|
ui.inviter("Penchez a GAUCHE, touchez")
|
||||||
ui.attendreToucher()
|
ui.attendreToucher()
|
||||||
x1, z1 = materiel.lireAnglesBruts()
|
x1, z1 = materiel.lireAnglesBruts()
|
||||||
|
|
@ -69,6 +73,7 @@ function Calibration.gimbal(conf, etat, materiel, journal, Etat, ui)
|
||||||
Etat.sauver(etat)
|
Etat.sauver(etat)
|
||||||
journal.info(("calib gimbal: tangage=angle%d(x%+d) roulis=angle%d(x%+d)")
|
journal.info(("calib gimbal: tangage=angle%d(x%+d) roulis=angle%d(x%+d)")
|
||||||
:format(indexTangage, signeTangage, indexRoulis, signeRoulis))
|
:format(indexTangage, signeTangage, indexRoulis, signeRoulis))
|
||||||
|
ui.progres(1, "terminee")
|
||||||
ui.inviter("Gimbal calibre, remettez a plat")
|
ui.inviter("Gimbal calibre, remettez a plat")
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
@ -105,7 +110,8 @@ function Calibration.joystick(conf, etat, materiel, journal, Etat, ui)
|
||||||
end
|
end
|
||||||
|
|
||||||
local exclues = {}
|
local exclues = {}
|
||||||
for _, direction in ipairs({ "devant", "derriere", "gauche", "droite" }) do
|
for idx, direction in ipairs({ "devant", "derriere", "gauche", "droite" }) do
|
||||||
|
ui.progres((idx - 1) / 4, direction)
|
||||||
ui.inviter("Joystick a fond: " .. direction:upper())
|
ui.inviter("Joystick a fond: " .. direction:upper())
|
||||||
local face = nil
|
local face = nil
|
||||||
while not face do
|
while not face do
|
||||||
|
|
@ -121,6 +127,7 @@ function Calibration.joystick(conf, etat, materiel, journal, Etat, ui)
|
||||||
|
|
||||||
etat.joystick = mapping
|
etat.joystick = mapping
|
||||||
Etat.sauver(etat)
|
Etat.sauver(etat)
|
||||||
|
ui.progres(1, "terminee")
|
||||||
ui.inviter("Joystick calibre")
|
ui.inviter("Joystick calibre")
|
||||||
journal.info("calib joystick: terminee")
|
journal.info("calib joystick: terminee")
|
||||||
return true
|
return true
|
||||||
|
|
@ -143,7 +150,8 @@ function Calibration.typewriter(conf, etat, materiel, journal, Etat, ui)
|
||||||
|
|
||||||
local symboles = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
|
local symboles = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
|
||||||
"valider", "effacer" }
|
"valider", "effacer" }
|
||||||
for _, symbole in ipairs(symboles) do
|
for idx, symbole in ipairs(symboles) do
|
||||||
|
ui.progres((idx - 1) / #symboles, symbole)
|
||||||
ui.inviter("Typewriter: appuyez sur " .. symbole:upper())
|
ui.inviter("Typewriter: appuyez sur " .. symbole:upper())
|
||||||
local code = nil
|
local code = nil
|
||||||
while not code do
|
while not code do
|
||||||
|
|
@ -161,6 +169,7 @@ function Calibration.typewriter(conf, etat, materiel, journal, Etat, ui)
|
||||||
|
|
||||||
etat.typewriter = mapping
|
etat.typewriter = mapping
|
||||||
Etat.sauver(etat)
|
Etat.sauver(etat)
|
||||||
|
ui.progres(1, "terminee")
|
||||||
ui.inviter("Typewriter calibre")
|
ui.inviter("Typewriter calibre")
|
||||||
journal.info("calib typewriter: terminee")
|
journal.info("calib typewriter: terminee")
|
||||||
return true
|
return true
|
||||||
|
|
@ -181,20 +190,51 @@ function Calibration.rsc(conf, etat, materiel, journal, Etat, ui)
|
||||||
materiel.toutArreter()
|
materiel.toutArreter()
|
||||||
local C = conf.CALIB
|
local C = conf.CALIB
|
||||||
|
|
||||||
local nonAffectes = {}
|
-- RECALIBRATION COMPLETE: on repart de zero (recablage possible)
|
||||||
for nom in pairs(materiel.rsc) do
|
local anciensRoles = etat.roles
|
||||||
if not etat.roles[nom] then table.insert(nonAffectes, nom) end
|
etat.roles = {}
|
||||||
end
|
materiel.rafraichirAffectations()
|
||||||
table.sort(nonAffectes)
|
|
||||||
if #nonAffectes == 0 then
|
local noms = {}
|
||||||
journal.info("calib rsc: tous les RSC sont deja affectes")
|
for nom in pairs(materiel.rsc) do table.insert(noms, nom) end
|
||||||
return true
|
table.sort(noms)
|
||||||
|
|
||||||
|
-- reference "a plat" pour le retour a l'etat initial entre mesures
|
||||||
|
sleep(1.0)
|
||||||
|
local refT, refR = materiel.lireAssiette()
|
||||||
|
local function retourInitial()
|
||||||
|
local fin = os.clock() + 10
|
||||||
|
while os.clock() < fin do
|
||||||
|
local t, r = materiel.lireAssiette()
|
||||||
|
if math.abs(t - refT) < C.IMPULSION_SEUIL / 2
|
||||||
|
and math.abs(r - refR) < C.IMPULSION_SEUIL / 2 then
|
||||||
|
sleep(0.5)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
sleep(0.2)
|
||||||
|
end
|
||||||
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function abandonner(raison)
|
||||||
|
materiel.toutArreter()
|
||||||
|
etat.roles = anciensRoles
|
||||||
|
materiel.rafraichirAffectations()
|
||||||
|
journal.erreur("calib rsc annulee: " .. raison)
|
||||||
|
ui.progres(0, "ECHEC: " .. raison)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local affectes = {} -- role -> nom (controle d'unicite)
|
||||||
local propulseurs = {}
|
local propulseurs = {}
|
||||||
for _, nom in ipairs(nonAffectes) do
|
|
||||||
|
for i, nom in ipairs(noms) do
|
||||||
|
ui.progres((i - 1) / #noms, "impulsion " .. nom)
|
||||||
|
if not retourInitial() then
|
||||||
|
return abandonner("assiette instable avant " .. nom)
|
||||||
|
end
|
||||||
|
|
||||||
journal.info("calib rsc: impulsion sur " .. nom)
|
journal.info("calib rsc: impulsion sur " .. nom)
|
||||||
ui.inviter("Impulsion: " .. nom)
|
|
||||||
local p = materiel.rsc[nom]
|
local p = materiel.rsc[nom]
|
||||||
local t0, r0 = materiel.lireAssiette()
|
local t0, r0 = materiel.lireAssiette()
|
||||||
local vitesse, dt_, dr = 0, 0, 0
|
local vitesse, dt_, dr = 0, 0, 0
|
||||||
|
|
@ -211,20 +251,26 @@ function Calibration.rsc(conf, etat, materiel, journal, Etat, ui)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
p.setTargetSpeed(0)
|
p.setTargetSpeed(0)
|
||||||
sleep(1.0)
|
|
||||||
|
|
||||||
if math.abs(dt_) >= C.IMPULSION_SEUIL
|
if math.abs(dt_) >= C.IMPULSION_SEUIL
|
||||||
and math.abs(dr) >= C.IMPULSION_SEUIL then
|
and math.abs(dr) >= C.IMPULSION_SEUIL then
|
||||||
-- assiette normalisee: tangage>0 = nez haut => helice a l'AVANT
|
-- assiette normalisee: tangage>0 = nez haut => helice a l'AVANT
|
||||||
-- roulis>0 = penche a droite => helice a GAUCHE
|
-- roulis>0 = penche a droite => helice a GAUCHE
|
||||||
local role = ((dr > 0) and "l" or "r") .. ((dt_ > 0) and "f" or "b")
|
local role = ((dr > 0) and "l" or "r") .. ((dt_ > 0) and "f" or "b")
|
||||||
|
if affectes[role] then
|
||||||
|
return abandonner(("conflit: %s et %s -> %s")
|
||||||
|
:format(affectes[role], nom, role))
|
||||||
|
end
|
||||||
|
affectes[role] = nom
|
||||||
etat.roles[nom] = role
|
etat.roles[nom] = role
|
||||||
|
ui.rscViolet(role)
|
||||||
journal.info(" -> helice " .. role)
|
journal.info(" -> helice " .. role)
|
||||||
else
|
else
|
||||||
table.insert(propulseurs, nom)
|
table.insert(propulseurs, nom)
|
||||||
journal.info(" -> pas d'effet d'assiette: propulseur")
|
journal.info(" -> pas d'effet d'assiette: propulseur")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
ui.rscViolet(nil)
|
||||||
|
|
||||||
if #propulseurs == 2 then
|
if #propulseurs == 2 then
|
||||||
etat.roles[propulseurs[1]] = "prop_l"
|
etat.roles[propulseurs[1]] = "prop_l"
|
||||||
|
|
@ -232,13 +278,14 @@ function Calibration.rsc(conf, etat, materiel, journal, Etat, ui)
|
||||||
journal.alerte("calib rsc: prop_l/prop_r affectes ARBITRAIREMENT: "
|
journal.alerte("calib rsc: prop_l/prop_r affectes ARBITRAIREMENT: "
|
||||||
.. "si un virage gauche part a droite, les echanger dans drone.etat")
|
.. "si un virage gauche part a droite, les echanger dans drone.etat")
|
||||||
elseif #propulseurs > 0 then
|
elseif #propulseurs > 0 then
|
||||||
journal.alerte(("calib rsc: %d propulseur(s), 2 attendus")
|
return abandonner(("%d propulseur(s) detecte(s), 2 attendus")
|
||||||
:format(#propulseurs))
|
:format(#propulseurs))
|
||||||
end
|
end
|
||||||
|
|
||||||
Etat.sauver(etat)
|
Etat.sauver(etat)
|
||||||
journal.info("calib rsc: terminee (relancer drone pour recharger)")
|
materiel.rafraichirAffectations() -- prise d'effet immediate
|
||||||
ui.inviter("RSC calibres: relancer drone")
|
ui.progres(1, "terminee")
|
||||||
|
journal.info("calib rsc: terminee")
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -286,26 +333,35 @@ function Calibration.capteurs(conf, etat, materiel, journal, Etat, ui,
|
||||||
|
|
||||||
local function signeDe(moyenne) return (moyenne >= 0) and 1 or -1 end
|
local function signeDe(moyenne) return (moyenne >= 0) and 1 or -1 end
|
||||||
|
|
||||||
|
-- RECALIBRATION COMPLETE
|
||||||
|
etat.velocite = {}
|
||||||
|
materiel.rafraichirAffectations()
|
||||||
|
|
||||||
|
ui.progres(0, "montee...")
|
||||||
ui.inviter("Calib capteurs: montee...")
|
ui.inviter("Calib capteurs: montee...")
|
||||||
local capteur, moyenne = mesurerPendant(mouvements.monter, {})
|
local capteur, moyenne = mesurerPendant(mouvements.monter, {})
|
||||||
etat.velocite[capteur] = { axe = "vertical", signe = signeDe(moyenne) }
|
etat.velocite[capteur] = { axe = "vertical", signe = signeDe(moyenne) }
|
||||||
journal.info((" vertical: %s (x%+d)"):format(capteur, signeDe(moyenne)))
|
journal.info((" vertical: %s (x%+d)"):format(capteur, signeDe(moyenne)))
|
||||||
local exclus = { [capteur] = true }
|
local exclus = { [capteur] = true }
|
||||||
|
|
||||||
|
ui.progres(0.33, "avancee...")
|
||||||
ui.inviter("Calib capteurs: avancee...")
|
ui.inviter("Calib capteurs: avancee...")
|
||||||
capteur, moyenne = mesurerPendant(mouvements.avancer, exclus)
|
capteur, moyenne = mesurerPendant(mouvements.avancer, exclus)
|
||||||
etat.velocite[capteur] = { axe = "avant", signe = signeDe(moyenne) }
|
etat.velocite[capteur] = { axe = "avant", signe = signeDe(moyenne) }
|
||||||
journal.info((" avant: %s (x%+d)"):format(capteur, signeDe(moyenne)))
|
journal.info((" avant: %s (x%+d)"):format(capteur, signeDe(moyenne)))
|
||||||
exclus[capteur] = true
|
exclus[capteur] = true
|
||||||
|
|
||||||
|
ui.progres(0.66, "translation gauche...")
|
||||||
ui.inviter("Calib capteurs: translation gauche...")
|
ui.inviter("Calib capteurs: translation gauche...")
|
||||||
capteur, moyenne = mesurerPendant(mouvements.gauche, exclus)
|
capteur, moyenne = mesurerPendant(mouvements.gauche, exclus)
|
||||||
etat.velocite[capteur] = { axe = "lateral", signe = signeDe(moyenne) }
|
etat.velocite[capteur] = { axe = "lateral", signe = signeDe(moyenne) }
|
||||||
journal.info((" lateral: %s (x%+d)"):format(capteur, signeDe(moyenne)))
|
journal.info((" lateral: %s (x%+d)"):format(capteur, signeDe(moyenne)))
|
||||||
|
|
||||||
Etat.sauver(etat)
|
Etat.sauver(etat)
|
||||||
ui.inviter("Capteurs calibres: relancer drone")
|
materiel.rafraichirAffectations()
|
||||||
journal.info("calib capteurs: terminee (relancer drone pour recharger)")
|
ui.progres(1, "terminee")
|
||||||
|
ui.inviter("Capteurs calibres")
|
||||||
|
journal.info("calib capteurs: terminee")
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -313,7 +369,7 @@ end
|
||||||
-- PID D'ALTITUDE (en vol stationnaire, marge d'altitude requise)
|
-- PID D'ALTITUDE (en vol stationnaire, marge d'altitude requise)
|
||||||
--------------------------------------------------------------------
|
--------------------------------------------------------------------
|
||||||
function Calibration.pid(conf, etat, materiel, pilotage, journal, Etat,
|
function Calibration.pid(conf, etat, materiel, pilotage, journal, Etat,
|
||||||
empreinte)
|
empreinte, ui)
|
||||||
local C = conf.CALIB
|
local C = conf.CALIB
|
||||||
local base = materiel.lireAltitude()
|
local base = materiel.lireAltitude()
|
||||||
local gains = pilotage.gains()
|
local gains = pilotage.gains()
|
||||||
|
|
@ -363,6 +419,9 @@ function Calibration.pid(conf, etat, materiel, pilotage, journal, Etat,
|
||||||
local meilleurs, meilleurTemps = nil, math.huge
|
local meilleurs, meilleurTemps = nil, math.huge
|
||||||
|
|
||||||
for iter = 1, C.MAX_ITER do
|
for iter = 1, C.MAX_ITER do
|
||||||
|
local base01 = (dir.sens > 0) and 0 or 0.5
|
||||||
|
ui.progres(base01 + (iter - 1) / C.MAX_ITER / 2,
|
||||||
|
("%s %d/%d"):format(dir.nom, iter, C.MAX_ITER))
|
||||||
journal.info(("[calib %s %d/%d] kp=%.1f ki=%.1f kd=%.1f")
|
journal.info(("[calib %s %d/%d] kp=%.1f ki=%.1f kd=%.1f")
|
||||||
:format(dir.nom, iter, C.MAX_ITER, g.kp, g.ki, g.kd))
|
:format(dir.nom, iter, C.MAX_ITER, g.kp, g.ki, g.kd))
|
||||||
pilotage.reglerGains(gains)
|
pilotage.reglerGains(gains)
|
||||||
|
|
@ -414,6 +473,7 @@ function Calibration.pid(conf, etat, materiel, pilotage, journal, Etat,
|
||||||
if math.abs(alt - base) <= C.BANDE then break end
|
if math.abs(alt - base) <= C.BANDE then break end
|
||||||
sleep(dt)
|
sleep(dt)
|
||||||
end
|
end
|
||||||
|
ui.progres(1, "terminee")
|
||||||
journal.info("calib pid: terminee")
|
journal.info("calib pid: terminee")
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,8 @@ return {
|
||||||
STRESS_BAS = 0.60, -- en deca: reduire le moteur (economie)
|
STRESS_BAS = 0.60, -- en deca: reduire le moteur (economie)
|
||||||
SURCHARGE = 0.98, -- au dela: reduire les propulseurs + alerte
|
SURCHARGE = 0.98, -- au dela: reduire les propulseurs + alerte
|
||||||
PERIODE = 1.0, -- periode de l'asservissement (s)
|
PERIODE = 1.0, -- periode de l'asservissement (s)
|
||||||
|
BOOST = 4.0, -- pre-boost a pleine puissance lors des
|
||||||
|
-- changements de regime exigeants (s)
|
||||||
},
|
},
|
||||||
|
|
||||||
------------------------------------------------------------------
|
------------------------------------------------------------------
|
||||||
|
|
|
||||||
347
lib/ihm.lua
347
lib/ihm.lua
|
|
@ -1,43 +1,80 @@
|
||||||
--------------------------------------------------------------------
|
--------------------------------------------------------------------
|
||||||
-- lib/ihm.lua : interfaces homme-machine.
|
-- lib/ihm.lua : interfaces homme-machine.
|
||||||
-- Display Link "conduite" : altitudes, vitesse, invites, alertes
|
-- Moniteur tactile : navigation par onglets (MODE / CALIB /
|
||||||
-- Display Link "carburant" : reserve + autonomie estimee (minutes)
|
-- JOURNAL), echelle de texte auto (la plus grande qui loge le
|
||||||
-- Advanced monitor tactile : modes (verrouilles si calibration
|
-- contenu), pages adaptees au mode, art du quadricoptere.
|
||||||
-- manquante), consigne, calibrations, informations
|
-- Display Link conduite : altitudes / vitesse / messages longs
|
||||||
-- Typewriter : la saisie est geree dans drone.lua (scrutation)
|
-- defiles par segments de mots entiers.
|
||||||
|
-- Display Link carburant : % + autonomie.
|
||||||
--------------------------------------------------------------------
|
--------------------------------------------------------------------
|
||||||
local Ihm = {}
|
local Ihm = {}
|
||||||
|
|
||||||
|
-- couleur d'un rpm: blanc=0, rouge=max, jaune=en changement, vert=stable
|
||||||
|
local function couleurRpm(suivi, vmax)
|
||||||
|
if not suivi then return colors.lightGray end
|
||||||
|
if os.clock() - suivi.tChange < 1.0 then return colors.yellow end
|
||||||
|
if suivi.v == 0 then return colors.white end
|
||||||
|
if math.abs(suivi.v) >= vmax then return colors.red end
|
||||||
|
return colors.green
|
||||||
|
end
|
||||||
|
|
||||||
function Ihm.nouveau(conf, materiel, journal)
|
function Ihm.nouveau(conf, materiel, journal)
|
||||||
local ihm = {}
|
local ihm = {}
|
||||||
local moniteur = materiel.moniteur
|
local moniteur = materiel.moniteur
|
||||||
moniteur.setTextScale(0.5)
|
|
||||||
|
|
||||||
local zones = {}
|
local zones = {}
|
||||||
local messageConduite, messageFin = nil, 0
|
local page = "mode" -- mode | calib | journal
|
||||||
|
local L, H = 0, 0 -- taille en caracteres
|
||||||
|
|
||||||
|
-- Echelle auto: la PLUS GRANDE echelle offrant au moins 38x14
|
||||||
|
local function reglerEchelle()
|
||||||
|
for _, s in ipairs({ 5, 4.5, 4, 3.5, 3, 2.5, 2, 1.5, 1, 0.5 }) do
|
||||||
|
moniteur.setTextScale(s)
|
||||||
|
L, H = moniteur.getSize()
|
||||||
|
if L >= 38 and H >= 14 then return end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
reglerEchelle()
|
||||||
|
|
||||||
|
------------------------------------------------------------------
|
||||||
|
-- MESSAGES DE CONDUITE (defilement par segments de mots entiers)
|
||||||
|
------------------------------------------------------------------
|
||||||
|
local segments, segmentFin, segmentIdx = nil, 0, 1
|
||||||
|
|
||||||
-- message temporaire sur la ligne 1 de la conduite (invites,
|
|
||||||
-- alertes); duree en secondes (nil = 5)
|
|
||||||
function ihm.message(texte, duree)
|
function ihm.message(texte, duree)
|
||||||
messageConduite = texte
|
if texte == "" then segments = nil return end
|
||||||
messageFin = os.clock() + (duree or 5)
|
-- decoupe en segments <= 22 caracteres sans couper les mots
|
||||||
|
segments = {}
|
||||||
|
local courant = ""
|
||||||
|
for mot in texte:gmatch("%S+") do
|
||||||
|
if #courant == 0 then courant = mot
|
||||||
|
elseif #courant + 1 + #mot <= 22 then courant = courant .. " " .. mot
|
||||||
|
else table.insert(segments, courant) courant = mot end
|
||||||
|
end
|
||||||
|
if #courant > 0 then table.insert(segments, courant) end
|
||||||
|
segmentIdx = 1
|
||||||
|
segmentFin = os.clock() + (duree or math.max(5, #segments * 2))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function segmentCourant()
|
||||||
|
if not segments or os.clock() > segmentFin then return nil end
|
||||||
|
-- avance d'un segment toutes les 2 s
|
||||||
|
local i = 1 + math.floor((os.clock() % (#segments * 2)) / 2)
|
||||||
|
return segments[math.min(i, #segments)]
|
||||||
|
.. (#segments > 1 and (" " .. i .. "/" .. #segments) or "")
|
||||||
end
|
end
|
||||||
|
|
||||||
------------------------------------------------------------------
|
|
||||||
-- ECRAN DE CONDUITE
|
|
||||||
------------------------------------------------------------------
|
|
||||||
-- ctx: { mode, consigneY, saisie }
|
-- ctx: { mode, consigneY, saisie }
|
||||||
function ihm.rafraichirConduite(ctx)
|
function ihm.rafraichirConduite(ctx)
|
||||||
local e = materiel.ecranConduite
|
local e = materiel.ecranConduite
|
||||||
e.clear()
|
e.clear()
|
||||||
e.setCursorPos(1, 1)
|
e.setCursorPos(1, 1)
|
||||||
if messageConduite and os.clock() < messageFin then
|
local seg = segmentCourant()
|
||||||
e.write(messageConduite:sub(1, 22))
|
if seg then
|
||||||
|
e.write(seg:sub(1, 22))
|
||||||
elseif ctx.saisie and #ctx.saisie > 0 then
|
elseif ctx.saisie and #ctx.saisie > 0 then
|
||||||
e.write("SAISIE: " .. ctx.saisie)
|
e.write(("SAISIE: " .. ctx.saisie):sub(1, 22))
|
||||||
elseif ctx.mode == "vol" or ctx.mode == "drone"
|
elseif ctx.mode == "vol" or ctx.mode == "drone"
|
||||||
or ctx.mode == "atterrissage" or ctx.mode == "auto"
|
or ctx.mode == "atterrissage" or ctx.mode == "auto" then
|
||||||
or ctx.mode == "position" then
|
|
||||||
e.write(("ALT %.1f / %.0f"):format(
|
e.write(("ALT %.1f / %.0f"):format(
|
||||||
materiel.lireAltitude(), ctx.consigneY or 0))
|
materiel.lireAltitude(), ctx.consigneY or 0))
|
||||||
else
|
else
|
||||||
|
|
@ -48,10 +85,6 @@ function Ihm.nouveau(conf, materiel, journal)
|
||||||
e.update()
|
e.update()
|
||||||
end
|
end
|
||||||
|
|
||||||
------------------------------------------------------------------
|
|
||||||
-- ECRAN CARBURANT (peut etre tres etroit: format compact)
|
|
||||||
------------------------------------------------------------------
|
|
||||||
-- pct 0..1 ou nil ; minutes ou nil
|
|
||||||
function ihm.rafraichirCarburant(pct, minutes)
|
function ihm.rafraichirCarburant(pct, minutes)
|
||||||
local e = materiel.ecranCarburant
|
local e = materiel.ecranCarburant
|
||||||
local largeur = e.getSize()
|
local largeur = e.getSize()
|
||||||
|
|
@ -62,23 +95,22 @@ function Ihm.nouveau(conf, materiel, journal)
|
||||||
else
|
else
|
||||||
e.write(("%d%%"):format(math.floor(pct * 100 + 0.5)):sub(1, largeur))
|
e.write(("%d%%"):format(math.floor(pct * 100 + 0.5)):sub(1, largeur))
|
||||||
e.setCursorPos(1, 2)
|
e.setCursorPos(1, 2)
|
||||||
if minutes then
|
e.write((minutes and ("%dm"):format(math.floor(minutes)) or "--m")
|
||||||
e.write(("%dm"):format(math.floor(minutes)):sub(1, largeur))
|
:sub(1, largeur))
|
||||||
else
|
|
||||||
e.write(("--m"):sub(1, largeur))
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
e.update()
|
e.update()
|
||||||
end
|
end
|
||||||
|
|
||||||
------------------------------------------------------------------
|
------------------------------------------------------------------
|
||||||
-- MONITEUR TACTILE
|
-- PRIMITIVES MONITEUR
|
||||||
------------------------------------------------------------------
|
------------------------------------------------------------------
|
||||||
local function bouton(x, y, texte, action, couleur)
|
local function bouton(x, y, texte, action, couleur, couleurTexte)
|
||||||
moniteur.setCursorPos(x, y)
|
moniteur.setCursorPos(x, y)
|
||||||
moniteur.setBackgroundColor(couleur or colors.gray)
|
moniteur.setBackgroundColor(couleur or colors.gray)
|
||||||
|
moniteur.setTextColor(couleurTexte or colors.white)
|
||||||
moniteur.write(" " .. texte .. " ")
|
moniteur.write(" " .. texte .. " ")
|
||||||
moniteur.setBackgroundColor(colors.black)
|
moniteur.setBackgroundColor(colors.black)
|
||||||
|
moniteur.setTextColor(colors.white)
|
||||||
table.insert(zones, {
|
table.insert(zones, {
|
||||||
x1 = x, y1 = y, x2 = x + #texte + 1, y2 = y, action = action,
|
x1 = x, y1 = y, x2 = x + #texte + 1, y2 = y, action = action,
|
||||||
})
|
})
|
||||||
|
|
@ -91,78 +123,245 @@ function Ihm.nouveau(conf, materiel, journal)
|
||||||
moniteur.setTextColor(colors.white)
|
moniteur.setTextColor(colors.white)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- ctx: { mode, consigneY, stress, position, carburantPct, minutes,
|
------------------------------------------------------------------
|
||||||
-- moteurOk, verrous = { mode -> raison|nil }, points = n }
|
-- ART DU QUADRICOPTERE
|
||||||
function ihm.rafraichirMoniteur(ctx)
|
-- rectangle, 4 cercles aux coins (helices, rpm a cote), rpm des
|
||||||
zones = {}
|
-- propulseurs sur les cotes; violet = RSC en cours de calibration
|
||||||
moniteur.setBackgroundColor(colors.black)
|
------------------------------------------------------------------
|
||||||
moniteur.clear()
|
local function couleurDe(role, ctx)
|
||||||
|
if ctx.rscViolet == role then return colors.purple end
|
||||||
|
return couleurRpm(materiel.rpm[role], conf.VITESSE_RSC_MAX)
|
||||||
|
end
|
||||||
|
|
||||||
texte(1, 1, "== MODES == actuel: " .. ctx.mode)
|
local function rpmDe(role)
|
||||||
local modes = { "off", "inactif", "stationnement", "vol", "drone",
|
local s = materiel.rpm[role]
|
||||||
"atterrissage", "auto", "position" }
|
return s and tostring(s.v) or "?"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- occupe 7 lignes a partir de y; largeur ~ 30
|
||||||
|
local function dessinerArt(x, y, ctx)
|
||||||
|
local function coin(cx, cy, role, rpmAGauche)
|
||||||
|
texte(cx, cy, "O", couleurDe(role, ctx))
|
||||||
|
local r = rpmDe(role)
|
||||||
|
if rpmAGauche then texte(cx - #r - 1, cy, r, couleurDe(role, ctx))
|
||||||
|
else texte(cx + 2, cy, r, couleurDe(role, ctx)) end
|
||||||
|
end
|
||||||
|
local g, d = x + 5, x + 18 -- colonnes des coins
|
||||||
|
texte(x + 9, y, "AVANT", colors.lightGray)
|
||||||
|
coin(g, y + 1, "lf", true)
|
||||||
|
coin(d, y + 1, "rf", false)
|
||||||
|
for i = 2, 4 do
|
||||||
|
texte(g, y + i, "|")
|
||||||
|
texte(d, y + i, "|")
|
||||||
|
end
|
||||||
|
texte(g + 1, y + 1, ("-"):rep(d - g - 1))
|
||||||
|
texte(g + 1, y + 5, ("-"):rep(d - g - 1))
|
||||||
|
texte(x, y + 3, rpmDe("prop_l"), couleurDe("prop_l", ctx))
|
||||||
|
texte(d + 2, y + 3, rpmDe("prop_r"), couleurDe("prop_r", ctx))
|
||||||
|
coin(g, y + 5, "lb", true)
|
||||||
|
coin(d, y + 5, "rb", false)
|
||||||
|
return y + 7
|
||||||
|
end
|
||||||
|
|
||||||
|
------------------------------------------------------------------
|
||||||
|
-- PAGE MODE (contenu adapte au mode courant)
|
||||||
|
------------------------------------------------------------------
|
||||||
|
local function blocCommun(y, ctx)
|
||||||
|
texte(1, y, ("carburant %s autonomie %s"):format(
|
||||||
|
ctx.carburantPct and ("%.0f%%"):format(ctx.carburantPct * 100)
|
||||||
|
or "n/a",
|
||||||
|
ctx.minutes and ("%dmin"):format(math.floor(ctx.minutes)) or "--"))
|
||||||
|
if ctx.position then
|
||||||
|
texte(1, y + 1, ("gps %.0f / %.0f"):format(
|
||||||
|
ctx.position.x, ctx.position.z))
|
||||||
|
else
|
||||||
|
texte(1, y + 1, "gps n/a", colors.red)
|
||||||
|
end
|
||||||
|
return y + 2
|
||||||
|
end
|
||||||
|
|
||||||
|
local function blocStress(y, ctx)
|
||||||
|
local brut, cap = materiel.lireStressBrut()
|
||||||
|
texte(1, y, ("stress %s / %s (%s) moteur %s"):format(
|
||||||
|
brut and ("%d"):format(brut) or "?",
|
||||||
|
cap and ("%d"):format(cap) or "?",
|
||||||
|
ctx.stress and ("%.0f%%"):format(ctx.stress * 100) or "n/a",
|
||||||
|
ctx.moteurOk and "ok" or "ARRETE"),
|
||||||
|
ctx.moteurOk and colors.white or colors.red)
|
||||||
|
return y + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
local function dessinerPageMode(ctx)
|
||||||
|
local modes = { "off", "inactif", "calibrage", "stationnement",
|
||||||
|
"vol", "drone", "atterrissage", "auto" }
|
||||||
local x, y = 1, 2
|
local x, y = 1, 2
|
||||||
for _, m in ipairs(modes) do
|
for _, m in ipairs(modes) do
|
||||||
local verrou = ctx.verrous[m]
|
local verrou = ctx.verrous[m]
|
||||||
local couleur = colors.gray
|
local couleur = colors.gray
|
||||||
if m == ctx.mode then couleur = colors.green
|
if m == ctx.mode then couleur = colors.green
|
||||||
elseif verrou then couleur = colors.red end
|
elseif verrou then couleur = colors.red end
|
||||||
if x + #m + 2 > 50 then x, y = 1, y + 1 end
|
if x + #m + 2 > L then x, y = 1, y + 1 end
|
||||||
bouton(x, y, m, { type = "mode", valeur = m, verrou = verrou },
|
bouton(x, y, m, { type = "mode", valeur = m, verrou = verrou },
|
||||||
couleur)
|
couleur)
|
||||||
x = x + #m + 3
|
x = x + #m + 3
|
||||||
end
|
end
|
||||||
|
y = y + 2
|
||||||
|
|
||||||
texte(1, y + 2, ("== CONSIGNE == %.0f"):format(ctx.consigneY or 0))
|
local mode = ctx.mode
|
||||||
bouton(1, y + 3, "-10", { type = "delta", valeur = -10 })
|
y = blocCommun(y, ctx)
|
||||||
bouton(7, y + 3, "-1", { type = "delta", valeur = -1 })
|
if mode ~= "off" then y = blocStress(y, ctx) end
|
||||||
bouton(12, y + 3, "+1", { type = "delta", valeur = 1 })
|
y = y + 1
|
||||||
bouton(17, y + 3, "+10", { type = "delta", valeur = 10 })
|
|
||||||
|
|
||||||
texte(1, y + 5, "== CALIBRATIONS ==")
|
if mode == "stationnement" or mode == "vol" or mode == "drone"
|
||||||
local cx = 1
|
or mode == "auto" then
|
||||||
|
y = dessinerArt(1, y, ctx)
|
||||||
|
end
|
||||||
|
|
||||||
|
if mode == "vol" or mode == "drone" then
|
||||||
|
texte(1, y, ("vit %.2f b/s alt %.1f -> %.0f"):format(
|
||||||
|
materiel.vitesseHorizontale(), materiel.lireAltitude(),
|
||||||
|
ctx.consigneY or 0))
|
||||||
|
y = y + 1
|
||||||
|
bouton(1, y, "-10", { type = "delta", valeur = -10 })
|
||||||
|
bouton(7, y, "-1", { type = "delta", valeur = -1 })
|
||||||
|
bouton(12, y, "+1", { type = "delta", valeur = 1 })
|
||||||
|
bouton(17, y, "+10", { type = "delta", valeur = 10 })
|
||||||
|
y = y + 1
|
||||||
|
if mode == "drone" then
|
||||||
|
texte(1, y, "INCLINAISON LIBRE (joystick = assiette)",
|
||||||
|
colors.orange)
|
||||||
|
y = y + 1
|
||||||
|
end
|
||||||
|
elseif mode == "atterrissage" then
|
||||||
|
texte(1, y, ("distance au sol: %.1f"):format(
|
||||||
|
materiel.lireDistanceSol()))
|
||||||
|
y = y + 1
|
||||||
|
elseif mode == "auto" then
|
||||||
|
texte(1, y, ("vit %.2f b/s alt %.1f cible %s dist %s"):format(
|
||||||
|
materiel.vitesseHorizontale(), materiel.lireAltitude(),
|
||||||
|
ctx.cible or "-", ctx.distanceCible
|
||||||
|
and ("%.0f"):format(ctx.distanceCible) or "-"))
|
||||||
|
y = y + 1
|
||||||
|
-- points enregistres en boutons + annulation
|
||||||
|
local x2 = 1
|
||||||
|
for nom in pairs(ctx.points or {}) do
|
||||||
|
if x2 + #nom + 2 > L - 10 then break end
|
||||||
|
bouton(x2, y, nom, { type = "ptgo", nom = nom }, colors.blue)
|
||||||
|
x2 = x2 + #nom + 3
|
||||||
|
end
|
||||||
|
bouton(L - 9, y, "ANNULER", { type = "annuler" }, colors.red)
|
||||||
|
y = y + 1
|
||||||
|
-- zone de saisie + pave numerique (coordonnees: "x z" ou "x y z")
|
||||||
|
texte(1, y, "coord: " .. (ctx.saisieCible or "") .. "_")
|
||||||
|
y = y + 1
|
||||||
|
local paves = { "1","2","3","4","5","6","7","8","9","0","-" }
|
||||||
|
local x3 = 1
|
||||||
|
for _, p in ipairs(paves) do
|
||||||
|
bouton(x3, y, p, { type = "pave", valeur = p })
|
||||||
|
x3 = x3 + 4
|
||||||
|
end
|
||||||
|
bouton(x3, y, "esp", { type = "pave", valeur = " " })
|
||||||
|
x3 = x3 + 6
|
||||||
|
bouton(x3, y, "eff", { type = "pave", valeur = "eff" })
|
||||||
|
x3 = x3 + 6
|
||||||
|
bouton(x3, y, "GO", { type = "pave", valeur = "go" }, colors.green)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
------------------------------------------------------------------
|
||||||
|
-- PAGE CALIBRATIONS
|
||||||
|
------------------------------------------------------------------
|
||||||
|
local function dessinerPageCalib(ctx)
|
||||||
|
texte(1, 2, "gris: a faire jaune: en cours vert: fait",
|
||||||
|
colors.lightGray)
|
||||||
|
local x, y = 1, 3
|
||||||
for _, c in ipairs({ "gimbal", "joystick", "typew", "rsc",
|
for _, c in ipairs({ "gimbal", "joystick", "typew", "rsc",
|
||||||
"capteurs", "pid" }) do
|
"capteurs", "pid" }) do
|
||||||
bouton(cx, y + 6, c, { type = "calib", valeur = c })
|
local statut = ctx.calibStatuts[c]
|
||||||
cx = cx + #c + 3
|
local couleur = colors.gray
|
||||||
|
if statut == "encours" then couleur = colors.yellow
|
||||||
|
elseif statut == "fait" then couleur = colors.green end
|
||||||
|
if x + #c + 2 > L then x, y = 1, y + 1 end
|
||||||
|
bouton(x, y, c, { type = "calib", valeur = c }, couleur,
|
||||||
|
statut == "encours" and colors.black or colors.white)
|
||||||
|
x = x + #c + 3
|
||||||
end
|
end
|
||||||
|
y = y + 2
|
||||||
|
|
||||||
local ly = y + 8
|
if ctx.calibTexte then
|
||||||
texte(1, ly, "== INFOS ==")
|
texte(1, y, ctx.calibTexte:sub(1, L), colors.yellow)
|
||||||
local t, r = materiel.lireAssiette()
|
y = y + 1
|
||||||
texte(1, ly + 1, ("assiette T %+.2f R %+.2f%s"):format(t, r,
|
|
||||||
ctx.gimbalOk and "" or " (NON CALIBRE)"))
|
|
||||||
texte(1, ly + 2, ("sol %.1f (%s%s)"):format(
|
|
||||||
materiel.lireDistanceSol(),
|
|
||||||
materiel.auSol() and "au sol" or "en vol",
|
|
||||||
materiel.solPosable() and "" or ", NON POSABLE"))
|
|
||||||
texte(1, ly + 3, ("stress %s moteur %s"):format(
|
|
||||||
ctx.stress and ("%.0f%%"):format(ctx.stress * 100) or "n/a",
|
|
||||||
ctx.moteurOk and "ok" or "ARRETE"),
|
|
||||||
ctx.moteurOk and colors.white or colors.red)
|
|
||||||
texte(1, ly + 4, ("carburant %s autonomie %s"):format(
|
|
||||||
ctx.carburantPct
|
|
||||||
and ("%.0f%%"):format(ctx.carburantPct * 100) or "n/a",
|
|
||||||
ctx.minutes and ("%dmin"):format(math.floor(ctx.minutes)) or "--"))
|
|
||||||
if ctx.position then
|
|
||||||
texte(1, ly + 5, ("gps %.0f %.0f %.0f points: %d")
|
|
||||||
:format(ctx.position.x, ctx.position.y, ctx.position.z,
|
|
||||||
ctx.points or 0))
|
|
||||||
else
|
|
||||||
texte(1, ly + 5, "gps n/a", colors.red)
|
|
||||||
end
|
end
|
||||||
|
if ctx.calibProgres then
|
||||||
|
local largeur = L - 2
|
||||||
|
local rempli = math.floor(ctx.calibProgres * largeur + 0.5)
|
||||||
|
moniteur.setCursorPos(1, y)
|
||||||
|
moniteur.setBackgroundColor(colors.green)
|
||||||
|
moniteur.write((" "):rep(rempli))
|
||||||
|
moniteur.setBackgroundColor(colors.gray)
|
||||||
|
moniteur.write((" "):rep(largeur - rempli))
|
||||||
|
moniteur.setBackgroundColor(colors.black)
|
||||||
|
y = y + 2
|
||||||
|
end
|
||||||
|
if ctx.rscViolet then
|
||||||
|
texte(1, y, "en violet: RSC en cours", colors.purple)
|
||||||
|
y = dessinerArt(1, y + 1, ctx)
|
||||||
|
end
|
||||||
|
texte(1, H - 1, "sol: gimbal joystick typew rsc (mode calibrage)",
|
||||||
|
colors.lightGray)
|
||||||
|
texte(1, H, "vol: capteurs pid (mode vol)", colors.lightGray)
|
||||||
|
end
|
||||||
|
|
||||||
|
------------------------------------------------------------------
|
||||||
|
-- PAGE JOURNAL
|
||||||
|
------------------------------------------------------------------
|
||||||
|
local function dessinerPageJournal()
|
||||||
|
local lignes = journal.recents(H - 2)
|
||||||
|
for i, ligne in ipairs(lignes) do
|
||||||
|
texte(1, 1 + i, ligne:sub(1, L),
|
||||||
|
ligne:find("ERREUR") and colors.red
|
||||||
|
or ligne:find("ALERTE") and colors.orange or colors.white)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
------------------------------------------------------------------
|
||||||
|
-- RENDU GLOBAL
|
||||||
|
------------------------------------------------------------------
|
||||||
|
function ihm.rafraichirMoniteur(ctx)
|
||||||
|
zones = {}
|
||||||
|
moniteur.setBackgroundColor(colors.black)
|
||||||
|
moniteur.clear()
|
||||||
|
|
||||||
|
-- barre d'onglets
|
||||||
|
local onglets = { mode = "MODE", calib = "CALIB", journal = "JOURNAL" }
|
||||||
|
local x = 1
|
||||||
|
for _, id in ipairs({ "mode", "calib", "journal" }) do
|
||||||
|
bouton(x, 1, onglets[id], { type = "page", valeur = id },
|
||||||
|
page == id and colors.green or colors.gray)
|
||||||
|
x = x + #onglets[id] + 3
|
||||||
|
end
|
||||||
|
texte(math.min(x + 1, L - #ctx.mode), 1, ctx.mode, colors.lightGray)
|
||||||
|
|
||||||
|
if page == "mode" then dessinerPageMode(ctx)
|
||||||
|
elseif page == "calib" then dessinerPageCalib(ctx)
|
||||||
|
else dessinerPageJournal() end
|
||||||
end
|
end
|
||||||
|
|
||||||
function ihm.traiterToucher(tx, ty)
|
function ihm.traiterToucher(tx, ty)
|
||||||
for _, z in ipairs(zones) do
|
for _, z in ipairs(zones) do
|
||||||
if tx >= z.x1 and tx <= z.x2 and ty >= z.y1 and ty <= z.y2 then
|
if tx >= z.x1 and tx <= z.x2 and ty >= z.y1 and ty <= z.y2 then
|
||||||
|
if z.action.type == "page" then
|
||||||
|
page = z.action.valeur
|
||||||
|
return { type = "redessiner" }
|
||||||
|
end
|
||||||
return z.action
|
return z.action
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
-- attend un toucher du moniteur (utilise par les calibrations)
|
function ihm.reglerPage(p) page = p end
|
||||||
|
|
||||||
function ihm.attendreToucher()
|
function ihm.attendreToucher()
|
||||||
while true do
|
while true do
|
||||||
local ev = os.pullEvent("monitor_touch")
|
local ev = os.pullEvent("monitor_touch")
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,20 @@
|
||||||
local Journal = {}
|
local Journal = {}
|
||||||
|
|
||||||
local FICHIER, MAX_TAILLE = "drone.log", 64 * 1024
|
local FICHIER, MAX_TAILLE = "drone.log", 64 * 1024
|
||||||
|
local recents, MAX_RECENTS = {}, 60
|
||||||
|
|
||||||
|
local function memoriser(ligne)
|
||||||
|
table.insert(recents, ligne)
|
||||||
|
if #recents > MAX_RECENTS then table.remove(recents, 1) end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- dernieres lignes (les plus recentes en dernier)
|
||||||
|
function Journal.recents(n)
|
||||||
|
local depart = math.max(1, #recents - (n or MAX_RECENTS) + 1)
|
||||||
|
local lignes = {}
|
||||||
|
for i = depart, #recents do table.insert(lignes, recents[i]) end
|
||||||
|
return lignes
|
||||||
|
end
|
||||||
|
|
||||||
local function horodater(texte)
|
local function horodater(texte)
|
||||||
return ("[%s] %s"):format(textutils.formatTime(os.time("local"), true), texte)
|
return ("[%s] %s"):format(textutils.formatTime(os.time("local"), true), texte)
|
||||||
|
|
@ -25,18 +39,21 @@ function Journal.info(texte)
|
||||||
local ligne = horodater(texte)
|
local ligne = horodater(texte)
|
||||||
print(ligne)
|
print(ligne)
|
||||||
ecrireFichier(ligne)
|
ecrireFichier(ligne)
|
||||||
|
memoriser(ligne)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Journal.erreur(texte)
|
function Journal.erreur(texte)
|
||||||
local ligne = horodater("ERREUR " .. texte)
|
local ligne = horodater("ERREUR " .. texte)
|
||||||
printError(ligne)
|
printError(ligne)
|
||||||
ecrireFichier(ligne)
|
ecrireFichier(ligne)
|
||||||
|
memoriser(ligne)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Journal.alerte(texte)
|
function Journal.alerte(texte)
|
||||||
local ligne = horodater("ALERTE " .. texte)
|
local ligne = horodater("ALERTE " .. texte)
|
||||||
printError(ligne)
|
printError(ligne)
|
||||||
ecrireFichier(ligne)
|
ecrireFichier(ligne)
|
||||||
|
memoriser(ligne)
|
||||||
end
|
end
|
||||||
|
|
||||||
return Journal
|
return Journal
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,9 @@ end
|
||||||
|
|
||||||
function Materiel.initialiser(conf, etat, journal)
|
function Materiel.initialiser(conf, etat, journal)
|
||||||
local m = {}
|
local m = {}
|
||||||
|
m.rpm = {} -- role -> { v, tChange } (pour l'affichage)
|
||||||
|
local posCache = nil, nil
|
||||||
|
local posDate = 0
|
||||||
|
|
||||||
m.altitude = trouver("altitude_sensor", true)
|
m.altitude = trouver("altitude_sensor", true)
|
||||||
m.gimbal = trouver("gimbal_sensor", true)
|
m.gimbal = trouver("gimbal_sensor", true)
|
||||||
|
|
@ -48,27 +51,31 @@ function Materiel.initialiser(conf, etat, journal)
|
||||||
m.ecranConduite = ecran(conf.ECRANS.conduite, "conduite")
|
m.ecranConduite = ecran(conf.ECRANS.conduite, "conduite")
|
||||||
m.ecranCarburant = ecran(conf.ECRANS.carburant, "carburant")
|
m.ecranCarburant = ecran(conf.ECRANS.carburant, "carburant")
|
||||||
|
|
||||||
-- rotation speed controllers
|
-- rotation speed controllers et velocity sensors: les affectations
|
||||||
m.rsc, m.rscParRole = {}, {}
|
-- (etat.roles / etat.velocite) sont rechargeables A CHAUD apres
|
||||||
|
-- une recalibration via m.rafraichirAffectations()
|
||||||
|
m.rsc, m.velocite = {}, {}
|
||||||
for _, nom in ipairs(peripheral.getNames()) do
|
for _, nom in ipairs(peripheral.getNames()) do
|
||||||
if peripheral.getType(nom) == "Create_RotationSpeedController" then
|
local type_ = peripheral.getType(nom)
|
||||||
|
if type_ == "Create_RotationSpeedController" then
|
||||||
m.rsc[nom] = peripheral.wrap(nom)
|
m.rsc[nom] = peripheral.wrap(nom)
|
||||||
local role = etat.roles[nom]
|
elseif type_ == "velocity_sensor" then
|
||||||
if role then m.rscParRole[role] = m.rsc[nom] end
|
m.velocite[nom] = peripheral.wrap(nom)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- velocity sensors
|
function m.rafraichirAffectations()
|
||||||
m.velocite, m.veloParAxe = {}, {}
|
m.rscParRole, m.veloParAxe = {}, {}
|
||||||
for _, nom in ipairs(peripheral.getNames()) do
|
for nom, p in pairs(m.rsc) do
|
||||||
if peripheral.getType(nom) == "velocity_sensor" then
|
local role = etat.roles[nom]
|
||||||
m.velocite[nom] = peripheral.wrap(nom)
|
if role then m.rscParRole[role] = p end
|
||||||
|
end
|
||||||
|
for nom, p in pairs(m.velocite) do
|
||||||
local aff = etat.velocite[nom]
|
local aff = etat.velocite[nom]
|
||||||
if aff then
|
if aff then m.veloParAxe[aff.axe] = { p = p, signe = aff.signe } end
|
||||||
m.veloParAxe[aff.axe] = { p = m.velocite[nom], signe = aff.signe }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
m.rafraichirAffectations()
|
||||||
|
|
||||||
-- cuves de carburant (create:fluid_tank; exclut le moteur diesel,
|
-- cuves de carburant (create:fluid_tank; exclut le moteur diesel,
|
||||||
-- qui est fluid_storage mais pas fluid_tank)
|
-- qui est fluid_storage mais pas fluid_tank)
|
||||||
|
|
@ -177,6 +184,12 @@ function Materiel.initialiser(conf, etat, journal)
|
||||||
return m.stresso.getStress() / cap
|
return m.stresso.getStress() / cap
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- valeurs brutes (su) pour l'affichage: stress, capacite
|
||||||
|
function m.lireStressBrut()
|
||||||
|
if not m.stresso then return nil end
|
||||||
|
return m.stresso.getStress(), m.stresso.getStressCapacity()
|
||||||
|
end
|
||||||
|
|
||||||
-- capacite > 0 <=> le moteur tourne (verifie: capacite 0 a l'arret)
|
-- capacite > 0 <=> le moteur tourne (verifie: capacite 0 a l'arret)
|
||||||
function m.moteurTourne()
|
function m.moteurTourne()
|
||||||
if not m.stresso then return true end -- indetectable: on suppose oui
|
if not m.stresso then return true end -- indetectable: on suppose oui
|
||||||
|
|
@ -197,11 +210,21 @@ function Materiel.initialiser(conf, etat, journal)
|
||||||
return quantite, capacite
|
return quantite, capacite
|
||||||
end
|
end
|
||||||
|
|
||||||
-- position GPS corrigee des offsets, ou nil
|
-- ACTUALISATION GPS (BLOQUANTE, jusqu'a 2 s): a appeler depuis une
|
||||||
function m.lirePosition()
|
-- tache dediee uniquement
|
||||||
|
function m.actualiserPosition()
|
||||||
local x, y, z = gps.locate(2)
|
local x, y, z = gps.locate(2)
|
||||||
if not x then return nil end
|
if x then
|
||||||
return x + conf.OFFSET_X, y + conf.OFFSET_Y, z + conf.OFFSET_Z
|
posCache = { x = x + conf.OFFSET_X, y = y + conf.OFFSET_Y,
|
||||||
|
z = z + conf.OFFSET_Z }
|
||||||
|
posDate = os.clock()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- position GPS en CACHE (non bloquante); nil si absente ou > 8 s
|
||||||
|
function m.lirePosition()
|
||||||
|
if not posCache or os.clock() - posDate > 8 then return nil end
|
||||||
|
return posCache.x, posCache.y, posCache.z
|
||||||
end
|
end
|
||||||
|
|
||||||
------------------------------------------------------------------
|
------------------------------------------------------------------
|
||||||
|
|
@ -214,11 +237,20 @@ function Materiel.initialiser(conf, etat, journal)
|
||||||
|
|
||||||
function m.reglerRsc(role, vitesse)
|
function m.reglerRsc(role, vitesse)
|
||||||
local p = m.rscParRole[role]
|
local p = m.rscParRole[role]
|
||||||
if p then p.setTargetSpeed(math.floor(vitesse + 0.5)) end
|
if not p then return end
|
||||||
|
vitesse = math.floor(vitesse + 0.5)
|
||||||
|
local suivi = m.rpm[role]
|
||||||
|
if not suivi or suivi.v ~= vitesse then
|
||||||
|
m.rpm[role] = { v = vitesse, tChange = os.clock() }
|
||||||
|
end
|
||||||
|
p.setTargetSpeed(vitesse)
|
||||||
end
|
end
|
||||||
|
|
||||||
function m.toutArreter()
|
function m.toutArreter()
|
||||||
for _, p in pairs(m.rsc) do p.setTargetSpeed(0) end
|
for _, p in pairs(m.rsc) do p.setTargetSpeed(0) end
|
||||||
|
for role, suivi in pairs(m.rpm) do
|
||||||
|
if suivi.v ~= 0 then m.rpm[role] = { v = 0, tChange = os.clock() } end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
------------------------------------------------------------------
|
------------------------------------------------------------------
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue