跳到教程内容
教程首页
App Inventor 2 教程 手机测高仪
准备开始
动手创造 · PROJECT LAB

手机测高仪

准备探索你的新作品
0%
★★☆ 项目难度每一步,都离作品更近

先了解你将做出什么

应用介绍

学校门口那棵大树有多高?对面的楼有几层楼那么高?

爬上去量显然不现实。但只要站在远处瞄一眼,手机就能算出来——这就是这一篇要做的东西。

教程主图

界面示意图

这个App用到的数学,就是数学课上讲的正切(tan)。 很多同学学的时候觉得”这玩意儿有什么用”,做完这个App你就知道了: 测量员、建筑师、林业工人量高度,用的就是这个办法。

手机测高仪教程(难度系数:★★☆)

原理:一个直角三角形

想象你站在树前面,眼睛盯着树顶:

  • 离树的水平距离,记作 d(这个你自己量,走过去数步子也行)。
  • 你的视线和水平方向的夹角,叫仰角,记作 θ(这个交给手机的传感器读)。
  • 你的眼睛离地面的高度,记作 h(一般比身高矮 10 厘米左右)。

你的眼睛、树顶、以及树上和你眼睛齐平的那一点,正好组成一个直角三角形

在这个三角形里,仰角的对边(树顶比你眼睛高出来的那一段)和邻边(水平距离 d)的比值, 就是 tan θ。所以:

树高 = d × tan(θ) + h

d × tan(θ) 算出的是”比你眼睛高出来的部分”,再加上你眼睛离地的高度 h,才是树的总高。

因为三角形的底边是从你的眼睛出发的,不是从地面出发的。

算出来的 d × tan(θ) 只是树顶比你眼睛高出来的那一截, 你眼睛以下还有一米五左右的树干呢,得补上。

漏掉这一项,量什么都会矮一米多——这是做这类测量App最常见的错误。

认识方向传感器1

方向传感器1能告诉你手机现在是怎么摆着的,它给三个角度:

属性 含义 这个App用不用
方位角 手机朝向哪个方向(东南西北),做指南针用这个 不用
倾斜角 手机前后翻的角度,抬头低头就是它在变 用这个
翻转角 手机左右歪的角度 不用

我们要的就是 倾斜角(英文 pitch)。手机平放时它接近 0, 把手机立起来往上抬,这个数就会变大。

先把传感器的读数显示出来

不要急着套公式。 做传感器相关的App,第一步永远是”先把读数打到屏幕上看看”, 搞清楚它到底怎么变,再决定怎么用。

global 当前仰角 = 0
global 已锁定 = false

when 方向传感器1.OrientationChanged(azimuth, pitch, roll) {
  if not (已锁定) {
    当前仰角 = abs(pitch)
    标签_角度.Text = join("当前仰角:", round(当前仰角), "°")
  }
}

方向改变时 这个事件,只要手机一动就会触发,数字会实时跳。

这里用了 abs() 取绝对值,原因是:不同手机、不同握法,倾斜角可能是正数也可能是负数。 我们只关心”抬起了多少度”,不关心正负,取绝对值最省事。

先把这几行写好,连上 AI伴侣,然后:

  1. 手机平放在桌上,看看显示多少度?(应该接近 0)
  2. 手机竖起来,屏幕正对自己,看看多少度?(应该接近 90)
  3. 慢慢抬起手机,数字是变大还是变小?

每台手机都值得这样试一遍。 如果你发现抬起来数字反而变小, 说明你的手机方向和我们假设的相反,把 abs(pitch) 改成 90 - abs(pitch) 就对了。

这就是为什么要”先看读数再写公式”——传感器这东西,不实测是不能想当然的。

锁定读数

瞄准树顶的时候,眼睛得盯着树,没法同时看屏幕;而且手一移开去点按钮,角度就变了。

所以要有个”锁定“的动作:对准之后点一下按钮,数字就定住不动了。

已锁定 这个变量就是干这个的——上面那段代码里, 只有 not (已锁定) 成立时才更新 当前仰角。锁定之后传感器照样在报数,但我们不理它了。

when 按钮_测量.Click() {
  if 已锁定 {
    已锁定 = false
    按钮_测量.Text = "锁定读数"
    标签_结果.Text = "对准目标顶端,然后点按钮"
  } else {
    已锁定 = true
    按钮_测量.Text = "重新瞄准"
    算高度()
  }
}

一个按钮管两件事:没锁定时点它就锁定并计算,已锁定时点它就解锁重新瞄。 按钮上的字也跟着变,用户不用猜。

套公式算高度

procedure 算高度() {
  if isempty(文本输入框_距离.Text) {
    标签_结果.Text = "先填上你离目标有多远(米)"
  } else if 当前仰角 >= 89 {
    标签_结果.Text = "角度太接近垂直了,往后退几步再量"
  } else {
    标签_结果.Text = join("目标大约高 ",
                        decimals(文本输入框_距离.Text * tan(当前仰角)
                                 + choose(isempty(文本输入框_眼高.Text), 1.5, 文本输入框_眼高.Text), 1),
                        " 米")
  }
}

三个分支,两个是在防止算出离谱的结果

  1. 距离没填就算不了,先提醒。
  2. 仰角接近 90 度tan 会飙到无穷大——你几乎站在树底下往上看, 算出来会是几千米。这时候该提醒用户往后退,而不是给一个荒唐的数字。
  3. 眼高没填就默认 1.5 米,用 choose(条件, 甲, 乙) 一行搞定,不用再写一个 if。

decimals(数值, 1) 是把结果保留一位小数——量个树高报到小数点后八位,没有意义还难看。

tan(89°) ≈ 57tan(89.9°) ≈ 573tan(90°) 直接是无穷大。

也就是说你站得离树越近、抬头越狠,误差被放大得越厉害。 稍微抖一下手,结果可能差出几十米。

所以真正的测量作业里有个经验:仰角控制在 30~60 度之间最准。 太小了三角形太扁,太大了 tan 太敏感。你可以把这个建议也做进App里提醒用户。

开始测试

先拿已知高度的东西校准——这是所有测量工具的第一步。

  1. 找一面墙或者一个门框,先用尺子量出真实高度
  2. 退后几米(记下距离),用App瞄准顶端,锁定,看看算出来多少。
  3. 和真实高度对比,差多少?

按这个顺序查:

  1. 眼高填了吗? 没填就是默认 1.5,和你的实际眼高差多少?
  2. 距离量准了吗? 这是误差最大的来源。走路数步子大概每步 0.6~0.75 米, 最好用卷尺或者手机上的测距App量。
  3. 手机拿稳了吗? 点按钮的一瞬间手一抖,角度就变了。可以把手机靠在什么东西上。
  4. 角度是不是太大? 退远一点,让仰角落在 30~60 度之间再量。

校准准了之后,再去量大树和楼房。

后记

这个App的精度大概能到 ±10%,量一棵十米的树误差在一米以内—— 对于”这树大概多高”这个问题,完全够用了。

想更准的话,专业的测距仪用的是激光测距 + 角度传感器, 把”距离”这个最大的误差源也自动化了。原理和我们这个是一样的,就是这个三角形。

几个可以继续挖的方向:

  • 量宽度。转 90 度用同样的道理,可以量出一栋楼有多宽。
  • 两次测量法。如果连距离都不方便量,可以在两个位置各测一次仰角, 用两个方程解出高度——这样连尺子都不用了,去查查”两角测高法”。
  • 记录本。把每次测量的地点和结果存进微数据库,做成一个”我量过的高度”清单。
  • 做成指南针。方向传感器1的 方位角 你还没用过,加个指针图片跟着转, 一个App就有了两个功能。

下次路过一棵特别高的树,掏出手机量一下——用自己写的程序去测量真实世界, 是编程里最有意思的事情之一ヾ(◍°∇°◍)ノ゙