零基础学 ArkUI 传感器(专题二):从加速度计到指南针,玩转硬件能力

📡 零基础学 ArkUI 传感器(专题二):从加速度计到指南针,玩转硬件能力

博主说: 你的手机里藏着十几个传感器------加速度计、陀螺仪、地磁、光线、距离、气压......在 ArkUI 中调用它们只需要几行代码!今天这篇专题带你一次摸透 HarmonyOS 的传感器全家桶,并实战做一个「水平仪」和「电子罗盘」。


📱 传感器能做什么?

传感器 检测什么 应用场景
加速度计 ACCELEROMETER 三轴加速度 计步器、摇一摇、屏幕旋转
陀螺仪 GYROSCOPE 角速度 VR 头显、体感游戏
地磁 MAGNETIC_FIELD 磁场强度 电子罗盘、指南针
环境光 AMBIENT_LIGHT 光照强度 lux 自动亮度调节
距离 PROXIMITY 物体靠近 通话熄屏
气压 BAROMETER 大气压力 海拔测量
心率 HEART_RATE 心跳 健康监测

⚙️ 运行环境要求

项目 要求
设备要求 真机!(模拟器不支持传感器)
DevEco Studio 5.0.3.800+
HarmonyOS SDK API 12+
核心 API @ohos.sensor

🛠️ 4 个实战案例

🎯 案例 1:加速度计 --- 做一个「摇一摇」检测

typescript 复制代码
import sensor from '@ohos.sensor';

@Entry
@Component
struct ShakeDetector {
  @State shakeCount: number = 0;
  @State lastShakeTime: number = 0;
  @State isShaking: boolean = false;

  aboutToAppear() {
    this.startListening();
  }

  startListening() {
    sensor.on(sensor.SensorType.ACCELEROMETER, (data) => {
      // 计算三轴加速度的矢量和
      const x = data.x;
      const y = data.y;
      const z = data.z;
      const magnitude = Math.sqrt(x*x + y*y + z*z);

      // 加速度 > 15 m/s² 视为一次摇动(重力 ≈ 9.8)
      if (magnitude > 15) {
        const now = Date.now();
        if (now - this.lastShakeTime > 500) { // 500ms 内只算一次
          this.lastShakeTime = now;
          this.shakeCount++;
          this.isShaking = true;
          // 300ms 后取消摇动状态
          setTimeout(() => { this.isShaking = false; }, 300);
        }
      }
    });
  }

  build() {
    Column() {
      Text(this.isShaking ? '📳 正在摇动!' : '📱 摇动手机')
        .fontSize(24).fontWeight(FontWeight.Bold)
        .fontColor(this.isShaking ? '#FF3B30' : '#333')

      Text(`已摇动 ${this.shakeCount} 次`)
        .fontSize(16).fontColor('#888').margin({ top: 12 })

      Button('重置计数')
        .margin({ top: 20 }).backgroundColor('#E5E5EA').fontColor('#333')
        .onClick(() => { this.shakeCount = 0; })
    }
    .width('100%').height(200).justifyContent(FlexAlign.Center)
  }
}

核心 API 说明:

typescript 复制代码
// 订阅传感器数据(持续监听)
sensor.on(SensorType.ACCELEROMETER, callback);

// 取消订阅
sensor.off(SensorType.ACCELEROMETER, callback);

// 单次读取
sensor.once(SensorType.ACCELEROMETER, (data) => { });

真机运行:用力摇动手机,屏幕显示摇动次数。


🎯 案例 2:陀螺仪 --- 体感控制的旋转方块

typescript 复制代码
import sensor from '@ohos.sensor';

@Entry
@Component
struct GyroCube {
  @State rotateX: number = 0;
  @State rotateY: number = 0;
  @State rotateZ: number = 0;

  aboutToAppear() {
    sensor.on(sensor.SensorType.GYROSCOPE, (data) => {
      // 积分角速度得到角度
      this.rotateX += data.x * 0.1;
      this.rotateY += data.y * 0.1;
      this.rotateZ += data.z * 0.1;
    });
  }

  build() {
    Column() {
      Column() {
        Text('3D').fontSize(32).fontColor('#fff').fontWeight(FontWeight.Bold)
      }
      .width(120).height(120)
      .backgroundColor('#007AFF')
      .borderRadius(16)
      .rotate({ x: 1, angle: this.rotateX })
      .rotate({ y: 1, angle: this.rotateY })

      Text('转动手机控制方块旋转')
        .fontSize(14).fontColor('#999').margin({ top: 20 })
    }
    .width('100%').height(300).justifyContent(FlexAlign.Center)
  }
}

🎯 案例 3:地磁传感器 --- 电子指南针

typescript 复制代码
import sensor from '@ohos.sensor';

@Entry
@Component
struct Compass {
  @State heading: number = 0; // 0~360 度

  aboutToAppear() {
    sensor.on(sensor.SensorType.MAGNETIC_FIELD, (data) => {
      // 根据地磁三轴数据计算方位角
      const heading = Math.atan2(data.y, data.x) * 180 / Math.PI;
      this.heading = (heading + 360) % 360; // 转为 0~360
    });
  }

  getDirection(degree: number): string {
    if (degree < 22.5 || degree >= 337.5) return '北 ↑';
    if (degree < 67.5) return '东北 ↗';
    if (degree < 112.5) return '东 →';
    if (degree < 157.5) return '东南 ↘';
    if (degree < 202.5) return '南 ↓';
    if (degree < 247.5) return '西南 ↙';
    if (degree < 292.5) return '西 ←';
    return '西北 ↖';
  }

  build() {
    Column() {
      // 指南针表盘
      Circle()
        .width(200).height(200)
        .fill('#F0F4FF')
        .stroke('#007AFF').strokeWidth(3)

      Text('N')
        .fontSize(18).fontWeight(FontWeight.Bold).fontColor('#FF3B30')
        .position({ x: 100, y: 10 })

      // 指针
      Column()
        .width(4).height(80)
        .backgroundColor('#FF3B30')
        .borderRadius(2)
        .rotate({ angle: this.heading })

      // 度数显示
      Text(`${Math.round(this.heading)}°`)
        .fontSize(36).fontWeight(FontWeight.Bold)
        .fontColor('#333').margin({ top: 20 })

      Text(this.getDirection(this.heading))
        .fontSize(18).fontColor('#007AFF').margin({ top: 8 })
    }
    .width('100%').height(400).justifyContent(FlexAlign.Center)
  }
}

🎯 案例 4:综合实战 --- 水平仪 App

结合加速度计和陀螺仪,做一个气泡水平仪。

typescript 复制代码
import sensor from '@ohos.sensor';

@Entry
@Component
struct BubbleLevel {
  @State bubbleX: number = 0;
  @State bubbleY: number = 0;
  @State isLevel: boolean = true;

  aboutToAppear() {
    sensor.on(sensor.SensorType.ACCELEROMETER, (data) => {
      // 手机平放时,重力在 Z 轴 = 9.8,X/Y ≈ 0
      // X/Y 偏离 0 说明手机倾斜
      const threshold = 0.5;
      this.bubbleX = Math.max(-40, Math.min(40, -data.x * 8));
      this.bubbleY = Math.max(-40, Math.min(40, data.y * 8));
      this.isLevel = Math.abs(data.x) < threshold && Math.abs(data.y) < threshold;
    });
  }

  build() {
    Column() {
      Text(this.isLevel ? '✅ 水平' : '❌ 倾斜')
        .fontSize(32).fontWeight(FontWeight.Bold)
        .fontColor(this.isLevel ? '#34C759' : '#FF3B30')

      // 水平仪容器
      Stack() {
        // 外圈
        Circle().width(200).height(200)
          .fill('#F0F4FF').stroke('#007AFF').strokeWidth(2)

        // 十字线
        Divider().vertical(true).height(180).color('#E0E0E0')
        Divider().width(180).color('#E0E0E0')

        // 气泡
        Circle().width(24).height(24)
          .fill('#007AFF').opacity(0.7)
          .translate({ x: this.bubbleX, y: this.bubbleY })
      }
      .width(220).height(220)
      .margin({ top: 20 })

      Text(`偏移: X=${this.bubbleX.toFixed(1)}  Y=${this.bubbleY.toFixed(1)}`)
        .fontSize(14).fontColor('#999').margin({ top: 16 })
    }
    .width('100%').height(400).justifyContent(FlexAlign.Center)
  }
}

📊 传感器数据对比表

传感器 回调频率 数据维度 坐标系
ACCELEROMETER 50~200Hz x, y, z (m/s²) 设备坐标系
GYROSCOPE 50~200Hz x, y, z (rad/s) 设备坐标系
MAGNETIC_FIELD 10~50Hz x, y, z (μT) 设备坐标系
AMBIENT_LIGHT 1~10Hz intensity (lux) 单值
PROXIMITY 1~5Hz distance (cm) 单值

⚠️ 避坑指南

原因 正确做法
模拟器传感器没数据 模拟器没有硬件 必须用真机调试
传感器不触发回调 忘了 sensor.on() 订阅 aboutToAppear 中订阅
数据跳变太大 原始传感器噪声大 用滑动平均滤波(取最近 5 次均值)
陀螺仪角度飘移 积分累积误差 用地磁 + 加速度计做互补滤波
耗电快 传感器回调频率太高 不需要高频率时用 sensor.once()
后台不工作 传感器在后台被暂停 申请后台任务权限

🔥 最佳实践

  1. 滤波处理:原始传感器噪声大,用低通滤波或滑动平均
  2. 频率控制 :不需要高频率时用 setInterval 节流回调
  3. 真机调试:传感器开发必须真机,模拟器不支持
  4. 性能优化aboutToDisappear 中取消订阅,防止内存泄漏
  5. 权限申明 :部分传感器(心率等)需要 health 权限
  6. 传感器融合:结合加速度 + 陀螺仪 + 地磁得到更准确的姿态

🚀 扩展挑战

  1. 计步器:用加速度计检测步行步态(峰值检测算法)
  2. 手势识别:用陀螺仪识别「画圈」「挥动」等手势
  3. AR 水平仪:结合相机预览 + 水平仪做装修辅助工具
  4. 磁力计标定:用「画 8 字」方法校准地磁传感器


官方文档: HarmonyOS 应用开发文档

相关推荐
G_dou_1 小时前
Flutter三方库适配OpenHarmony【expense_tracker】消费记录器项目完整实战
flutter·harmonyos
进击的小头1 小时前
第8篇:IGBT 从零到精通:核心原理、关键参数、选型指南与工业级应用要点
经验分享·嵌入式硬件·学习
小陈phd2 小时前
Text2SQL智能体学习笔记(一)——NL2SQL及执行流程介绍
笔记·学习
风栖柳白杨2 小时前
【大模型学习】主流大模型统计
学习
FrameNotWork2 小时前
HarmonyOS6.1 从图像分类到目标检测的扩展实现
人工智能·harmonyos
lengxuemo2 小时前
ICC2学习之PG
学习
稷下元歌2 小时前
系统学习plc 基础指令上篇,官方资料课程笔记整 理
笔记·学习
我的xiaodoujiao2 小时前
API 接口自动化测试详细图文教程学习系列25--继续处理testCase中的数据
python·学习·测试工具·pytest
你的保护色2 小时前
数据库第一章-基础知识学习
数据库·学习