from IPython.display import HTML
HTML('''<script>
code_show=true;
function code_toggle() {
if (code_show){
$('div.input').hide();
} else {
$('div.input').show();
}
code_show = !code_show
}
$( document ).ready(code_toggle);
</script>
<form action="javascript:code_toggle()"><input type="submit" value="Visa / dölj källkoden."></form>''')
I en modern "smartphone" finns det sensorer som gör att den kan användas som mätinstrument både för rörelse och lufttryck. Jag tog med min iPhone, med appen SensorLog, på en hisstur fyra våningar upp och ned i en hiss. Med hjälp av programmeringsspråket Python, med bl a tillägget SciPy för numerisk analys, så kunde i efterhand de närmare 1 700 mätvärdena av respektive slag hanteras och användas för skapa såväl rörelse- och tryckförändringsgrafer samt för att göra beräkningar på dessa. Allt detta hanterades i den integrerade miljön Jupyter. Det är en öppen plattform som bl a kan köra kod, skapa diagram utifrån kod, visa texter och bilder samt slutligen skapa en fil som kan presentera resultatet av detta på webben.
Syftet med mätningen var att undersöka hur telefonens mätdata för rörelse och lufttryck var användbar för att göra grafer och beräkningar utifrån, och i så fall avgöra hur väl resultatet stämde överens med verkliga förhållanden och teoretiska modeller. Ett annat syfte var att undersöka hur väl Pythonmiljön lämpade sig för hantering av resultaten.
Frågeställningarna blev då
Mätningarna skedde den 24 april 2017 på Åva gymnasium i Täby. Det var regnigt ute, så det var inte förvånande att de uppmätta lufttrycken var lägre än normaltrycket på 101,325 kPa. Alla värden mättes upp i frekvensen 50 Hz. Rörelsen mättes enbart i en dimension.
Själva försöket lades upp så att telefonen, med appen SensorLog, låg på hissens golv. Innan hissen trycktes igång startades mätningen, varpå mätningen avslutades efter att hissen hade stannat. Mätningar gjordes då hissen åkte upp fyra våningar respektive ned lika långt. SensorLog samlade in accelerations- och tryckdata (och även annan data som är irrelevant i detta experiment) i en och samma mätning.
Databearbetningen gjordes med hjälp av programmeringsspråket Python på plattformen Jupyter. SensorLog ger data i form av ett tidsvärde samt accelerations- och tryckdata. Det behövs en funktionsanpassning och en numerisk integrationsmetod som kan tilämpas på accelerationsdatan för att få ut hastighet och läge på hissen vid de olika tidpunkterna; dessa verktyg finns tillgänglig i Pythontillägget SciPy. Vidare behövs möjlighet att plotta datan. Det ger biblioteket Matplotlib möjlighet till.
Datan representeras i tabellkolumner: för respektive tidpunkt så kommer det att finnas ett accelerationsvärde, hastighetsvärde, lägesvärde och tryckvärde.
import numpy as np # För grundläggande matrisfunktioner
import matplotlib.pyplot as plt # För att kunna rita grafer
import matplotlib.pylab as pylab # För att kunna sätta storleken på graferna
from numpy import genfromtxt # För att kunna läsa från CSV-filer och lägga i matriser
from scipy.interpolate import UnivariateSpline # För att kunna integrera
from scipy import stats # För att kunna utföra linjär regression
from IPython.core.display import display, HTML, Markdown # För att kunna skapa hyperlänkar som utdata
g = 9.81 # Värdet på tyngdaccelerationen i loggern
# Några inställningar för graferna
# Grafernas storlek
# (Man kan även sätta graferns storlek (t ex W = 15, H = 10) för varje plot med "plt.figure(figsize=(15,10))")
pylab.rcParams['figure.figsize'] = (15.0, 10.0)
# Fonter i graferna
fontHeader = {'family': 'serif',
'color': 'darkred',
'weight': 'normal',
'size': 24,
}
fontAxis = {'family': 'serif',
'color': 'black',
'weight': 'normal',
'size': 20,
}
# Tabeller skapas
# De tabeller som kommer att användas för att skapa graferna är
# * tValsDown (tiden i sekunder från start)
# * aValsDown (accelerationen vid respektive tidpunkt, korresponederar till tidpunkterna i tValsDown).
# * vValsDown (hastigheten vid respektive tidpunkt, korresponederar till tidpunkterna i tValsDown).
# * positionDown (läget vid respektive tidpunkt, korresponederar till tidpunkterna i tValsDown).
# * pValsDown (trycket vid respektive tidpunkt, korresponederar till tidpunkterna i tValsDown).
# * tValsDownRed (en tabell som innehåller vart 100:e tidsvärde, ger bättre resultat för tryckmätningarna)
# * pValsDownRed (tryckvärden, korresponderar till tidpunkterna i tValsDownRed)
# Läs in datan från fil och skapa tabeller av den
descent = genfromtxt('hiss_ned.csv', delimiter = ',')
ascent = genfromtxt('hiss_upp.csv', delimiter=',')
timestampsDown = descent[30:len(descent) - 50, 3] # Tidsmarkeringar, ligger fr o m rad 1 i kolumn 3. Startar dock på rad 30 och avslutar 50 rader innan mätslut för att få bort omkringliggande brus.
timestampsUp = ascent[1:len(ascent) - 50 ,3] # Tidsmarkeringar, ligger fr o m rad 1 i kolumn 3.
accstampsDown = descent[31:len(descent) - 50, 6] # Acc-värden. Då tidsstämplarna kommer att göras om till intervall så blir antalet sådana ett mindre än stämplarna. Därav så startar tabellern på rad 26 istället för 25 för att värdena ska bli lika många.
accstampsUp = ascent[2:len(ascent) - 50 ,6] # Acc-värden. Då tidsstämplarna kommer att göras om till intervall så blir antalet
# sådana ett mindre än stämplarna. Därav så startar tabellern på rad 2 istället för 1
# för att värdena ska bli lika många.
pValsDown = descent[31:len(descent) - 50, 10] # Tryckvärden
pValsUp = ascent[2:,10] # Tryckvärden
# I tabellen får vi enbart tidsstämplar, dessa måste göras om till en tidslängd från start.
# Dessa tidslängder läggs i en ny tabell. Antalet rader i denna tabell kommer att bli en mindre än timestampsDown-tabellen.
tValsDown = []
tValsUp = []
timeZeroDown = timestampsDown[0]
timeZeroUp = timestampsUp[0]
for time in timestampsDown[1:]:
tValsDown.append(time - timeZeroDown)
for time in timestampsUp[1:]:
tValsUp.append(time - timeZeroUp)
# Nedanstående tabeller tar ett urval av tids- och tryckvärdena. Detta för att samplingsfrekvensen
# egentligen är för hög för att mäta tryckskillnader.
tValsDownRed = [] # En "reducerad" tidstabell
tValsUpRed = [] # En "reducerad" tidstabell
for i in range(0, len(tValsDown), 100): # Tar vart 100:e värde
tValsDownRed.append(tValsDown[i])
for i in range(0, len(tValsUp), 100): # Tar vart 100:e värde
tValsUpRed.append(tValsUp[i])
pValsDownRed = [] # En "reducerad" trycktabell
for i in range(0, len(pValsDown), 100):
pValsDownRed.append(pValsDown[i])
pValsUpRed = [] # En "reducerad" tidstabell
for i in range(0, len(pValsUp), 100):
pValsUpRed.append(pValsUp[i])
# Accelerationen från tabellen är uttryckt i g-enheter med postiv riktning nedåt.
# Jag vill ha accelerationen i m/s2 med positiv riktning uppåt.
# Dessutom kompenseras för det absoluta felet i accelerometervärdet, det visar sig att "viloläget"
# ligger på -0.995 g istället för -1.00 g genom att utgå från summan av accelerationen = 0 under hela hissturen.
aValsDown = (accstampsDown - sum(accstampsDown) / len(accstampsDown)) * (-g)
aValsUp = (accstampsUp - sum(accstampsUp) / len(accstampsUp)) * (-g)
# Funktioner till mätvärdena måste approximeras för att kunna integreras,
# vilket måste göras för att få hastighet respektive läge.
# Accelerationsfunktionen approximeras
accelerationDown = UnivariateSpline(tValsDown, aValsDown)
accelerationUp = UnivariateSpline(tValsUp, aValsUp)
accelerationDown.set_smoothing_factor(1)
accelerationUp.set_smoothing_factor(1)
# Hastigheten beräknas med hjälp av accelerationsfunktionen ovan, och vilkoret v(0) = 0.
vValsDown = []
vValsUp = []
counter = 0
displacement = 0
for i in tValsDown:
if counter == 0:
vValsDown.append(accelerationDown.integral(tValsDown[counter], i))
else:
vValsDown.append(accelerationDown.integral(tValsDown[counter - 1], i) + vValsDown[counter - 1] )
counter = counter + 1
counter = 0
for i in tValsUp:
if counter == 0:
vValsUp.append(accelerationUp.integral(tValsUp[counter], i))
else:
vValsUp.append(accelerationUp.integral(tValsUp[counter - 1], i) + vValsUp[counter - 1] )
counter = counter + 1
# Hastighetsfunktionen approximeras
velocityDown = UnivariateSpline(tValsDown, vValsDown)
velocityUp = UnivariateSpline(tValsUp, vValsUp)
velocityDown.set_smoothing_factor(0.001)
velocityUp.set_smoothing_factor(0.001)
averageVelocityDown = sum(vValsDown) / len(vValsDown)
displacementDown = velocityDown.integral(0, tValsDown[len(tValsDown) - 1]) # Förflyttningen beräknas som integralen över tiden för hastighetsfunktionen
averageVelocityUp = sum(vValsUp) / len(vValsUp)
displacementUp = velocityUp.integral(0, tValsUp[len(tValsUp) - 1]) # Förflyttningen beräknas som integralen över tiden för hastighetsfunktionen
# Läget beräknas ur hastighetsfunktionen ovan med villkoret h(0) = högsta höjden.
positionDown = []
positionUp = []
counter = 0
for i in tValsDown:
if counter == 0:
positionDown.append(velocityDown.integral(tValsDown[counter], i) - velocityDown.integral(0, tValsDown[len(tValsDown) - 1]))
else:
positionDown.append(velocityDown.integral(tValsDown[counter - 1], i) + positionDown[counter - 1])
counter = counter + 1
counter = 0
for i in tValsUp:
if counter == 0:
positionUp.append(velocityUp.integral(tValsUp[counter], i))
else:
positionUp.append(velocityUp.integral(tValsUp[counter - 1], i) + positionUp[counter - 1] )
counter = counter + 1
# Någon lägesfunktion behöver inte approximeras, vi ska enbart rita grafen.
# Framförallt för trycket vill jag ha en tabell med färre mätvärden (jag väljer vart 100:e),
# därför att jag vill visa diskreta punkter istället för punkter som ligger så tätt att de ser ut som
# en sammanbunden linje. Däremot kommer jag att anpassa en rät linje till de punkter som skapats då
# hissen gick i konstant hastighet.
positionDownRed = []
positionUpRed = []
for i in range(0, len(positionDown), 100):
positionDownRed.append(positionDown[i])
for i in range(0, len(positionUp), 100):
positionUpRed.append(positionUp[i])
# En hjälpfunktion för att kunna göra urval baserat på tid istället för indexnummer
# Den används för att kunna använda tidsintervall istället för indexnummer när trycket under turen analyseras.
def timeSel(lowerTime, upperTime, useReducedTable = 0, down = 0):
indexMin = 0
indexMax = 0
numberOfValues = 1
timeSpan = []
if useReducedTable == 0:
if down == 0:
for theTime in tValsUp:
if theTime < lowerTime:
indexMin = indexMin + 1
if ((theTime <= upperTime) and (theTime > lowerTime)):
indexMax = indexMax + 1
numberOfValues = numberOfValues + 1
else:
for theTime in tValsDown:
if theTime < lowerTime:
indexMin = indexMin + 1
if ((theTime <= upperTime) and (theTime > lowerTime)):
indexMax = indexMax + 1
numberOfValues = numberOfValues + 1
if useReducedTable == 1:
if down == 0:
for theTime in tValsUpRed:
if theTime < lowerTime:
indexMin = indexMin + 1
if ((theTime <= upperTime) and (theTime > lowerTime)):
indexMax = indexMax + 1
numberOfValues = numberOfValues + 1
else:
if down == 1:
for theTime in tValsDownRed:
if theTime < lowerTime:
indexMin = indexMin + 1
if ((theTime <= upperTime) and (theTime > lowerTime)):
indexMax = indexMax + 1
numberOfValues = numberOfValues + 1
timeSpan.append(indexMin)
timeSpan.append(indexMax + indexMin)
timeSpan.append(numberOfValues)
# Returnerar t ex vektorn [10, 110, 101], vilket innebär att den nedre intervallgränsen ligger på
# index 10, den övre på 110 och att antal värden är 101 stycken.
return timeSpan
Den enda data som appen SensorLog gav beträffande rörelsen var accelerationen. Den datan erhålls i multipler av tyngdaccelerationen, som är förinställd i appen enligt amerikansk standard till 9,81 m/s2. Rådatan för uppfärden finns som csv-fil här och för nedfärden finns csv-filen här.
plt.tick_params(labelsize=16)
plt.plot(tValsUp, accstampsUp, 'r.')
plt.title('Accelerationsmätning under hisstur uppåt - plottade rådata\n', fontdict = fontHeader)
plt.xlabel('Tid [s]', fontdict = fontAxis)
plt.ylabel('Acceleration [$\cdot 9.81\;$m/s$^2$]', fontdict = fontAxis)
# Tweak spacing to prevent clipping of ylabel
plt.subplots_adjust(left=0.15)
plt.savefig("fig1a.png", transparent=True)
plt.show()
display(Markdown('**Figur 1a:** *Plottade rådata från accelerometer under hisstur uppåt.*'))
plt.tick_params(labelsize=16)
plt.plot(tValsDown, accstampsDown, 'r.')
plt.title('Accelerationsmätning under hisstur nedåt - plottade rådata\n', fontdict = fontHeader)
plt.xlabel('Tid [s]', fontdict = fontAxis)
plt.ylabel('Acceleration [$\cdot 9.81\;$m/s$^2$]', fontdict = fontAxis)
# Tweak spacing to prevent clipping of ylabel
plt.subplots_adjust(left=0.15)
plt.savefig("fig1b.png", transparent=True)
plt.show()
display(Markdown('**Figur 1b:** *Plottade rådata från accelerometer under hisstur nedåt.*'))
Jag väljer att representera accelerationen med positiv rikting uppåt och i absoluta tal istället för i multipler av g. Dessutom kompenseras för det absoluta felet i accelerometervärdet; det visar sig att "viloläget" ligger på -0.995g (syns i ovanstående graf) istället för -1.00g. Funktionen anpassas med hjälp av NumPy:s funktion UnivariateSpline
.
plt.tick_params(labelsize=16)
plt.plot(tValsUp, aValsUp, 'r.')
plt.plot(tValsUp, accelerationUp(tValsUp), 'b-', lw=3)
plt.grid(b=True, which='major', color='black', linestyle='--')
plt.gcf().subplots_adjust(left=0.15)
plt.title('Rörelsemätning under hisstur uppåt - Acceleration\n', fontdict = fontHeader)
plt.xlabel('Tid [s]', fontdict = fontAxis)
plt.ylabel('Acceleration [m/s$^2$]', fontdict = fontAxis)
# Tweak spacing to prevent clipping of ylabel
plt.subplots_adjust(left=0.15)
plt.savefig("fig2a.png", transparent=True)
plt.show()
display(Markdown('**Figur 2a:** *Anpassad accelerationsfunktion.*'))
plt.tick_params(labelsize=16)
plt.plot(tValsDown, aValsDown, 'r.')
plt.plot(tValsDown, accelerationDown(tValsDown), 'b-', lw=3)
plt.grid(b=True, which='major', color='black', linestyle='--')
plt.gcf().subplots_adjust(left=0.15)
plt.title('Rörelsemätning under hisstur nedåt - Acceleration\n', fontdict = fontHeader)
plt.xlabel('Tid [s]', fontdict = fontAxis)
plt.ylabel('Acceleration [m/s$^2$]', fontdict = fontAxis)
# Tweak spacing to prevent clipping of ylabel
plt.subplots_adjust(left=0.15)
plt.savefig("fig2b.png", transparent=True)
plt.show()
display(Markdown('**Figur 2b:** *Anpassad accelerationsfunktion.*'))
Genom att integrera respektive accelerationsfunktion över små tidsintervall erhålls hastighetsförändringen över respektive intervall. Givet att hissen startar från stillastående så kan hastigheten vid olika tidpunkter beräknas.
plt.tick_params(labelsize=16)
plt.plot(tValsUp, vValsUp, 'b', lw = 3)
plt.grid(b=True, which='major', color='black', linestyle='--')
plt.gcf().subplots_adjust(left=0.15)
plt.title('Rörelsemätning under hisstur uppåt - Hastighet\n', fontdict = fontHeader)
plt.xlabel('Tid [s]', fontdict = fontAxis)
plt.ylabel('Hastighet [m/s]', fontdict = fontAxis)
# Tweak spacing to prevent clipping of ylabel
plt.subplots_adjust(left=0.15)
plt.savefig("fig3a.png", transparent=True)
plt.show()
display(Markdown('**Figur 3a:** *Hastighet vid olika tidpunkter då hissen är på väg uppåt.*'))
print('Beräknad medelhastighet: {:.2f}'.format(averageVelocityUp), 'm/s.')
plt.tick_params(labelsize=16)
plt.plot(tValsDown, vValsDown, 'b', lw = 3)
plt.grid(b=True, which='major', color='black', linestyle='--')
plt.gcf().subplots_adjust(left=0.15)
plt.title('Rörelsemätning under hisstur nedåt - Hastighet\n', fontdict = fontHeader)
plt.xlabel('Tid [s]', fontdict = fontAxis)
plt.ylabel('Hastighet [m/s]', fontdict = fontAxis)
# Tweak spacing to prevent clipping of ylabel
plt.subplots_adjust(left=0.15)
plt.savefig("fig3b.png", transparent=True)
plt.show()
display(Markdown('**Figur 3b:** *Hastighet vid olika tidpunkter då hissen är på väg nedåt.*'))
print('Beräknad medelhastighet: {:.2f}'.format(averageVelocityDown), 'm/s.')
Genom att integrera hastighetsfunktionen över små tidsintervall från start och framåt så erhålls förflyttningen över det aktuella intervallet. Den kumulerade värdet utgör förflyttningen till det aktuella tidsintervallets slut. Värdet av integralen över hela intervallet utgör den totala förflyttningen i förhållande till bottenplanet. Abslolutbeloppet av den totala förflyttningen motsvarar höjden över bottenplanet.
plt.tick_params(labelsize=16)
plt.plot(tValsUp, positionUp, 'b', lw = 5)
plt.grid(b=True, which='major', color='black', linestyle='-')
plt.gcf().subplots_adjust(left=0.15)
plt.title('Rörelsemätning under hisstur uppåt - Höjd\n', fontdict = fontHeader)
plt.xlabel('Tid [s]', fontdict = fontAxis)
plt.ylabel('Höjd över bottenplan [m]', fontdict = fontAxis)
# Tweak spacing to prevent clipping of ylabel
plt.subplots_adjust(left=0.15)
plt.savefig("fig4a.png", transparent=True)
plt.show()
display(Markdown('**Figur 4a:** *Höjden vid olika tidpunkter.*'))
print('Beräknad förflyttning: {:.1f}'.format(displacementUp), 'm.')
plt.tick_params(labelsize=16)
plt.plot(tValsDown, positionDown, 'b', lw = 5)
plt.grid(b=True, which='major', color='black', linestyle='-')
plt.gcf().subplots_adjust(left=0.15)
plt.title('Rörelsemätning under hisstur nedåt - Höjd\n', fontdict = fontHeader)
plt.xlabel('Tid [s]', fontdict = fontAxis)
plt.ylabel('Höjd över bottenplan [m]', fontdict = fontAxis)
# Tweak spacing to prevent clipping of ylabel
plt.subplots_adjust(left=0.15)
plt.savefig("fig4b.png", transparent=True)
plt.show()
display(Markdown('**Figur 4b:** *Höjden vid olika tidpunkter.*'))
print('Beräknad förflyttning: {:.1f}'.format(displacementDown), 'm.')
Med
Antal trappsteg: 88
Höjd på trappsteg: 16,0 - 16,5 cm
så erhålls den totala höjden h: 14,1 m < h < 14,5 m (detta ska jämföras med respektive beräkna förflyttning ovan)
Tryckets variation med tiden räknas om till dess variation över den faktiska förflyttningen (enligt de beräknade data som ligger till grund för höjd-diagrammet ovan). För diagrammets del visade det sig vara fruktlöst att markera samtliga värden då mätning gjordes 50 gånger per sekund; trycket förändrades helt enkelt inte mätbart över ett så kort tidsintervall. Här reduceras antalet värden till 1 av 100, dvs ett mätvärde varannan sekund.
plt.tick_params(labelsize=16)
plt.plot(positionUpRed, pValsUpRed, 'rx', ms = 16)
#pylab.xlim(0, 15)
plt.grid(b=True, which='major', color='black', linestyle='--')
plt.gcf().subplots_adjust(left=0.15)
plt.title('Lufttrycksmätning under hisstur uppåt\n', fontdict = fontHeader)
plt.xlabel('Höjd över bottenplan [m]', fontdict = fontAxis)
plt.ylabel('Tryck [kPa]', fontdict = fontAxis)
# Tweak spacing to prevent clipping of ylabel
plt.subplots_adjust(left=0.15)
plt.savefig("fig5a.png", transparent=True)
plt.show()
display(Markdown('**Figur 5a:** *Lufttryckets variation med höjden under uppfärd.*'))
plt.gca().invert_xaxis()
plt.tick_params(labelsize=16)
plt.plot(positionDownRed, pValsDownRed, 'rx', ms = 16)
plt.grid(b=True, which='major', color='black', linestyle='--')
plt.gcf().subplots_adjust(left=0.15)
plt.title('Lufttrycksmätning under hisstur nedåt\n', fontdict = fontHeader)
plt.xlabel('Höjd över bottenplan [m]', fontdict = fontAxis)
plt.ylabel('Tryck [kPa]', fontdict = fontAxis)
# Tweak spacing to prevent clipping of ylabel
plt.subplots_adjust(left=0.15)
plt.savefig("fig5b.png", transparent=True)
plt.show()
display(Markdown('**Figur 5b:** *Lufttryckets variation med höjden under nedfärd.*'))
Nedan syns hur tryckförändringen per höjdenhet varierar med accelerationen. Det bör vara en konsekvens av luftens tröghet; den "hinner inte med" hissens acceleration. I denna datahantering är samtliga mätvärden medtagna i de redovisade intervallen.
timeIntStart = timeSel(0, 2.0, 0, 0)
timeIntMid = timeSel (5, 28, 0, 0)
timeIntStop = timeSel(30, 32, 0, 0)
posSelStart = positionUp[timeIntStart[0]:timeIntStart[1]]
pressureSelStart = pValsUp[timeIntStart[0]:timeIntStart[1]]
posSelMid = positionUp[timeIntMid[0]:timeIntMid[1]]
pressureSelMid = pValsUp[timeIntMid[0]:timeIntMid[1]]
posSelEnd = positionUp[timeIntStop[0]:timeIntStop[1]]
pressureSelEnd = pValsUp[timeIntStop[0]:timeIntStop[1]]
m1, b1, r1, p1, e1 = stats.linregress(posSelStart, pressureSelStart)
m2, b2, r2, p2, e2 = stats.linregress(posSelMid, pressureSelMid)
m3, b3, r3, p3, e3 = stats.linregress(posSelEnd, pressureSelEnd)
display(Markdown('#### Beräknade tryckförändringar per höjdenhet under uppfärd'))
print('*Startfas (0.0 - 2.0 sekunder)*')
print('h1: {:.2f}'.format(posSelStart[0]), 'm.')
print('h2: {:.2f}'.format(posSelStart[len(posSelStart) - 1]), 'm.')
print('Tryckförändring per höjdenhet: {:.2f}'.format(m1*1000), 'Pa/m')
print('R^2-värde: {:.2f}'.format(r1**2))
print('Antal mätvärden: {}'.format(timeIntStart[2]))
print('------------------------------------')
print('*Mellanfas (5.0 - 28.0 sekunder)*')
print('h1: {:.2f}'.format(posSelMid[0]), 'm.')
print('h2: {:.2f}'.format(posSelMid[len(posSelMid) - 1]), 'm.')
print('Tryckförändring per höjdenhet: {:.2f}'.format(m2*1000), 'Pa/m')
print('R^2-värde: {:.2f}'.format(r2**2))
print('Antal mätvärden: {}'.format(timeIntMid[2]))
print('------------------------------------')
print('*Slutfasfas (30.0 - 32.0 sekunder)*')
print('h1: {:.2f}'.format(posSelEnd[0]), 'm.')
print('h2: {:.2f}'.format(posSelEnd[len(posSelEnd) - 1]), 'm.')
print('Tryckförändring per höjdenhet: {:.2f}'.format(m3*1000), 'Pa/m')
print('R^2-värde: {:.2f}'.format(r3**2))
print('Antal mätvärden: {}'.format(timeIntStop[2]))
timeIntStart = timeSel(0, 2.0, 0, 1)
timeIntMid = timeSel (5, 28, 0, 1)
timeIntStop = timeSel(30, 32, 0, 1)
posSelStart = positionDown[timeIntStart[0]:timeIntStart[1]]
pressureSelStart = pValsDown[timeIntStart[0]:timeIntStart[1]]
posSelMid = positionDown[timeIntMid[0]:timeIntMid[1]]
pressureSelMid = pValsDown[timeIntMid[0]:timeIntMid[1]]
posSelEnd = positionDown[timeIntStop[0]:timeIntStop[1]]
pressureSelEnd = pValsDown[timeIntStop[0]:timeIntStop[1]]
m1, b1, r1, p1, e1 = stats.linregress(posSelStart, pressureSelStart)
m2, b2, r2, p2, e2 = stats.linregress(posSelMid, pressureSelMid)
m3, b3, r3, p3, e3 = stats.linregress(posSelEnd, pressureSelEnd)
display(Markdown('#### Beräknade tryckförändringar per höjdenhet under nedfärd'))
print('*Startfas (0.0 - 2.0 sekunder)*')
print('h1: {:.2f}'.format(posSelStart[0]), 'm.')
print('h2: {:.2f}'.format(posSelStart[len(posSelStart) - 1]), 'm.')
print('Tryckförändring per höjdenhet: {:.2f}'.format(m1*1000*(-1)), 'Pa/m')
print('R^2-värde: {:.2f}'.format(r1**2))
print('Antal mätvärden: {}'.format(timeIntStart[2]))
print('------------------------------------')
print('*Mellanfas (5.0 - 28.0 sekunder)*')
print('h1: {:.2f}'.format(posSelMid[0]), 'm.')
print('h2: {:.2f}'.format(posSelMid[len(posSelMid) - 1]), 'm.')
print('Tryckförändring per höjdenhet: {:.2f}'.format(m2*1000*(-1)), 'Pa/m')
print('R^2-värde: {:.2f}'.format(r2**2))
print('Antal mätvärden: {}'.format(timeIntMid[2]))
print('------------------------------------')
print('*Slutfasfas (30.0 - 32.0 sekunder)*')
print('h1: {:.2f}'.format(posSelEnd[0]), 'm.')
print('h2: {:.2f}'.format(posSelEnd[len(posSelEnd) - 1]), 'm.')
print('Tryckförändring per höjdenhet: {:.2f}'.format(m3*1000*(-1)), 'Pa/m')
print('R^2-värde: {:.2f}'.format(r3**2))
print('Antal mätvärden: {}'.format(timeIntStop[2]))
I diagrammet nedan visas den del av tryckförändringen som är linjär. Den visar sig hänga ihop med att hissen går i konstant hastighet.
plt.tick_params(labelsize=16)
timeInt = timeSel(2.5, 30, 1, 1)
timeTics = tValsUpRed[timeInt[0]:timeInt[1]]
positionTics = positionUpRed[timeInt[0]:timeInt[1]]
pressureMarks = pValsUpRed[timeInt[0]:timeInt[1]]
xi = np.arange(0, 15)
m, b, r, p, e = stats.linregress(positionTics, pressureMarks)
line = m * xi + b
plt.plot(positionTics, pressureMarks, 'rx', ms = 16)
plt.plot(line, linestyle = '-', color = 'b', linewidth = 3.0)
#pylab.xlim(0, 15)
plt.grid(b=True, which='major', color='k', linestyle='--')
plt.gcf().subplots_adjust(left=0.15)
plt.title('Lufttrycksmätning under hisstur uppåt\n', fontdict = fontHeader)
plt.xlabel('Höjd över bottenplan [m]', fontdict = fontAxis)
plt.ylabel('Tryck [kPa]', fontdict = fontAxis)
# Tweak spacing to prevent clipping of ylabel
plt.subplots_adjust(left=0.15)
plt.savefig("fig6a.png", transparent=True)
plt.show()
display(Markdown('**Figur 6a:** *Trycket minskar linjärt med ökad höjd nära marknivå. Mätresultat under den delen av hissturen som var i konstant hastighet.*'))
print('Beräknad tryckförändring per höjdenhet: {:.1f}'.format(m * 1000), 'Pa/m')
print('r^2-värde: {:.2f}'.format(r**2))
display(HTML("""<a target = blank href="https://www.smhi.se/kunskapsbanken/meteorologi/lufttryck-1.657">SMHI:s värde: ca -12.5 Pa/m, nära marknivå.</a>"""))
plt.gca().invert_xaxis()
plt.tick_params(labelsize=16)
timeInt = timeSel(5, 30, 1, 0)
timeTics = tValsDownRed[timeInt[0]:timeInt[1]]
positionTics = positionDownRed[timeInt[0]:timeInt[1]]
pressureMarks = pValsDownRed[timeInt[0]:timeInt[1]]
xi = np.arange(0, 15)
k, m, r, p, e = stats.linregress(positionTics, pressureMarks)
line = k * xi + m
plt.plot(positionTics, pressureMarks, 'rx', ms = 16)
plt.plot(line, linestyle = '-', color = 'b', linewidth = 3.0)
plt.grid(b=True, which='major', color='black', linestyle='--')
plt.gcf().subplots_adjust(left=0.15)
plt.title('Lufttrycksmätning under hisstur nedåt\n', fontdict = fontHeader)
plt.xlabel('Höjd över bottenplan [m]', fontdict = fontAxis)
plt.ylabel('Tryck [kPa]', fontdict = fontAxis)
# Tweak spacing to prevent clipping of ylabel
plt.subplots_adjust(left=0.15)
plt.savefig("fig6b.png", transparent=True)
plt.show()
display(Markdown('**Figur 6b:** *Trycket ökar linjärt med minskad höjd nära marknivå. Mätresultat under den delen av hissturen som var i konstant hastighet.*'))
print('Beräknad tryckförändring per höjdenhet: {:.1f}'.format(k * 1000*(-1)), 'Pa/m')
print('r^2-värde: {:.2f}'.format(r**2))
display(HTML("""<a target = blank href="https://www.smhi.se/kunskapsbanken/meteorologi/lufttryck-1.657">SMHI:s värde: ca 12.5 Pa/m, nära marknivå</a>."""))
Det som går att kontrollera med ovanstående mätningar är
Beträffande formen på de till mätvärdena anpassade funktionsgraferna så överenstämmer sekvenserna väl med motsvarande grafer som visar konstant hastighet respektive konstant acceleration (se valfri fysikbok fr o m gymnasienivå, rörelseavsnittet, eller denna sida på The Physics Hypertextbook). Så tillvida, om antagandet är att hissen startar och avslutar med likformig acceleration (i olika riktningar beroende på höjning eller sänkning av farten), verkar själva mätdatan och hanteringen av denna bekräfta dessa grundförutsättningar.
Beräkningen av höjden ligger inom, om än på den övre gränsen av felmarginalen i förhållande till kontrollmätning av trappsteg. Observera att det handlar om en avvikelse på några enstaka decimeter i förhållande till höjden på över 14 m; i storleksordning endast några enstaka procent!
Beträffande tryckvariationen så finns en smärre avvikelse, 4% under nedfärd och 9% under uppfärd, från SMHI:s värde.
Det var möjligt att hantera och redovisa mätdatan med hjälp av Python och Jupyter (denna webbsida utgör i sig det resultatet). Om det är rimligt beror framför allt på förkunskaperna i sådan programmering, som till viss del skiljer sig åt jämfört med applikationsprogrammering.
Det syns att den plottade rådatan varierar avsevärt även kring närliggande tidpunkter under den period som hissens hastighet är konstant. Detta skulle kunna bero på små vibrationer i hissen samt mätbrus. De, efter mätpunkterna, anpassade funktionerna ansluter dock till teoretiska modeller för likformig- och likformigt accelererad rörelse, även om det går att se en del "knyck" runt de tider som hastighetsändring sker. Det syns också att hastigheten är mer stabil under nedfärd än uppfärd under den tid som hastigheten bör kunna förväntas vara konstant; detta skulle kunna bero på att det är lättare för hissmotorn att bromsa än att lyfta så att hastigheten hålls konstant.
Det var även intressant att en förskjutning av mätvärdena fick göras, då accelerometern inte visade värdet för 1g i horisontellt viloläge. Denna avvikelse stämmer även överens med andra mätningar i andra miljöer med samma telefon.
Att höjdmätningen gav en noggranhet som avviker med maximalt ett par procent blev jag positivt överraskad av.
Även om tryckvariationen, under den tid som hissens hastighet är konstant, med avseende på höjd i detta försök avviker nedåt med 4% till 9% i förhållande till SMHI:s värde, så bygger SMHI sitt värde på ett genomsnitt. Det skulle kunna vara så att det värdet kan variera med aktuellt lufttryck, det behövs ytterligare undersökning på under varierade lufttrycksförhållanden.
Vi har också sett att hastigheten på tryckvariationen skiljer sig avsevärt från referensvärdet under hissens accelerationsfaser. Dessa avvikelser skulle kunna förklaras av luftens tröghet och turbulens som uppstår i samband med att luftmassan börjar röra sig. Under accelerationsfaserna såg vi även att R2-värdet blev lägre, vilket tyder på en sämre möjlig linjär anpassning. Personligen tycker jag att det är imponerande att trycksensorn i telefonen var så högupplöst att de små och snabba förändringarna gav såpass rimliga resultat.
Även om inte själva programmeringen inte innehåller några särskilt avancerade strukturer, så var det många detaljer att sätta sig in i första gången. Det gällde allt från tabellhantering till att få grafernas storlek i lämpliga proportioner och snygga axelrubriker.
Detta webbdokument är gjort med Jupyter; ett verktyg som integrerar kod, diagram, beräkningar, text och bild i ett och samma dokument. Mätningar, tolkningar och kod av Nikodemus Karlsson.