鸿蒙跨端噪音污染监测系统开发指南 原创
鸿蒙跨端噪音污染监测系统开发指南
一、项目概述
本文基于HarmonyOS的音频处理能力和分布式技术,开发一套智能噪音污染监测系统。该系统采用麦克风采样率自适应技术、优化的FFT频谱分析算法和数据分级存储策略,并借鉴《鸿蒙跨端U同步》中的多设备同步技术,实现环境噪音的精准监测、分析和多终端协同预警。
二、系统架构
±--------------------+       ±--------------------+       ±--------------------+
监测设备          <-----> 分布式数据总线 <-----> 监控终端
(智能终端/传感器) (Distributed Bus) (手机/平板/管理端)
±---------±---------+       ±---------±---------+       ±---------±---------+
±---------v----------+       ±---------v----------+       ±---------v----------+
音频采集模块       噪音分析模块 数据同步模块
(采样率自适应) (FFT优化/分级) (多设备协同/预警)
±--------------------+ ±--------------------+ ±--------------------+
三、核心代码实现
噪音监测服务实现
// src/main/ets/service/NoiseMonitorService.ts
import { distributedData } from ‘@ohos.data.distributedData’;
import { BusinessError } from ‘@ohos.base’;
import { audio } from ‘@ohos.multimedia.audio’;
import { power } from ‘@ohos.power’;
import { notification } from ‘@ohos.notification’;
import { taskpool } from ‘@ohos.taskpool’;
import { fileIo } from ‘@ohos.fileio’;
import { zlib } from ‘@ohos.zlib’;
interface NoiseData {
timestamp: number;
dB: number;              // 分贝值
frequencyPeak: number;  // 主要频率峰值 (Hz)
spectrum: number[];     // 频谱数据 (简化版)
location?: string;      // 位置信息
deviceId: string;
isSynced: boolean;
interface NoiseLevel {
timestamp: number;
level: ‘quiet’ ‘moderate’ ‘loud’ ‘very_loud’
‘extreme’;
duration: number;       // 持续时间(秒)
isSynced: boolean;
interface DeviceStatus {
batteryLevel: number;
isLowPowerMode: boolean;
sampleRate: number;     // 当前采样率 (Hz)
lastSyncTime: number;
export class NoiseMonitorService {
private static instance: NoiseMonitorService;
private kvStore: distributedData.KVStore | null = null;
private readonly STORE_ID = ‘noise_monitor_store’;
private audioCapturer: audio.AudioCapturer | null = null;
private noiseData: NoiseData[] = [];
private noiseLevels: NoiseLevel[] = [];
private deviceStatus: DeviceStatus = {
batteryLevel: 100,
isLowPowerMode: false,
sampleRate: 44100,    // 默认44.1kHz
lastSyncTime: 0
};
private rawAudioData: number[] = [];
private readonly SYNC_INTERVAL = 5  60  1000; // 5分钟同步一次
private constructor() {
this.initKVStore();
this.loadLocalData();
this.adjustSampleRate();
public static getInstance(): NoiseMonitorService {
if (!NoiseMonitorService.instance) {
  NoiseMonitorService.instance = new NoiseMonitorService();
return NoiseMonitorService.instance;
private async initKVStore(): Promise<void> {
try {
  const options: distributedData.KVManagerConfig = {
    bundleName: 'com.example.noisemonitor',
    userInfo: {
      userId: '0',
      userType: distributedData.UserType.SAME_USER_ID
};
  const kvManager = distributedData.createKVManager(options);
  this.kvStore = await kvManager.getKVStore({
    storeId: this.STORE_ID,
    options: {
      createIfMissing: true,
      encrypt: false,
      backup: false,
      autoSync: true,
      kvStoreType: distributedData.KVStoreType.SINGLE_VERSION
});
  this.kvStore.on('dataChange', distributedData.SubscribeType.SUBSCRIBE_TYPE_REMOTE, (data) => {
    this.handleRemoteDataChange(data);
  });
catch (e) {
  console.error(Failed to initialize KVStore. Code: {e.code}, message: {e.message});
}
private async loadLocalData(): Promise<void> {
try {
// 加载噪音数据
const noiseFile = await fileIo.open(‘data/noise_data.bin’, 0o666);
const noiseData = await fileIo.read(noiseFile.fd, new ArrayBuffer(0));
await fileIo.close(noiseFile.fd);
  if (noiseData) {
    const decompressed = await zlib.deflateSync(noiseData);
    this.noiseData = JSON.parse(String.fromCharCode.apply(null, new Uint8Array(decompressed)));
// 加载噪音等级记录
  const levelFile = await fileIo.open('data/noise_levels.bin', 0o666);
  const levelData = await fileIo.read(levelFile.fd, new ArrayBuffer(0));
  await fileIo.close(levelFile.fd);
  
  if (levelData) {
    const decompressed = await zlib.deflateSync(levelData);
    this.noiseLevels = JSON.parse(String.fromCharCode.apply(null, new Uint8Array(decompressed)));
// 加载设备状态
  const statusFile = await fileIo.open('data/device_status.bin', 0o666);
  const statusData = await fileIo.read(statusFile.fd, new ArrayBuffer(0));
  await fileIo.close(statusFile.fd);
  
  if (statusData) {
    const decompressed = await zlib.deflateSync(statusData);
    this.deviceStatus = JSON.parse(String.fromCharCode.apply(null, new Uint8Array(decompressed)));
} catch (e) {
  console.log('No local data found or error reading file');
}
private async saveLocalData(): Promise<void> {
try {
// 确保目录存在
await fileIo.mkdir(‘data’);
  // 保存噪音数据
  const noiseStr = JSON.stringify(this.noiseData);
  const noiseCompressed = await zlib.inflateSync(new Uint8Array(noiseStr.split('').map(c => c.charCodeAt(0))));
  
  const noiseFile = await fileIo.open('data/noise_data.bin', 0o666 | fileIo.OpenMode.CREATE);
  await fileIo.write(noiseFile.fd, noiseCompressed.buffer);
  await fileIo.close(noiseFile.fd);
  
  // 保存噪音等级
  const levelStr = JSON.stringify(this.noiseLevels);
  const levelCompressed = await zlib.inflateSync(new Uint8Array(levelStr.split('').map(c => c.charCodeAt(0))));
  
  const levelFile = await fileIo.open('data/noise_levels.bin', 0o666 | fileIo.OpenMode.CREATE);
  await fileIo.write(levelFile.fd, levelCompressed.buffer);
  await fileIo.close(levelFile.fd);
  
  // 保存设备状态
  const statusStr = JSON.stringify(this.deviceStatus);
  const statusCompressed = await zlib.inflateSync(new Uint8Array(statusStr.split('').map(c => c.charCodeAt(0))));
  
  const statusFile = await fileIo.open('data/device_status.bin', 0o666 | fileIo.OpenMode.CREATE);
  await fileIo.write(statusFile.fd, statusCompressed.buffer);
  await fileIo.close(statusFile.fd);
catch (e) {
  console.error(Failed to save local data. Code: {e.code}, message: {e.message});
}
private async adjustSampleRate(): Promise<void> {
// 根据电池状态调整采样率
await this.checkBatteryStatus();
let newSampleRate = 44100; // 默认44.1kHz
if (this.deviceStatus.isLowPowerMode) {
  newSampleRate = 16000; // 低电量模式使用16kHz
else {
  // 根据最近噪音水平动态调整
  if (this.noiseLevels.length > 0) {
    const lastLevel = this.noiseLevels[this.noiseLevels.length - 1];
    if (lastLevel.level = 'quiet' || lastLevel.level = 'moderate') {
      newSampleRate = 22050; // 安静环境降低采样率
else if (lastLevel.level === ‘extreme’) {
      newSampleRate = 48000; // 极端噪音提高采样率
}
if (newSampleRate !== this.deviceStatus.sampleRate) {
  this.deviceStatus.sampleRate = newSampleRate;
  await this.restartAudioCapture();
}
private async checkBatteryStatus(): Promise<void> {
try {
const batteryInfo = await power.getBatteryInfo();
this.deviceStatus.batteryLevel = batteryInfo.batterySoc;
  // 低电量模式切换 (低于30%电量)
  const shouldLowPower = batteryInfo.batterySoc < 30;
  if (shouldLowPower !== this.deviceStatus.isLowPowerMode) {
    this.deviceStatus.isLowPowerMode = shouldLowPower;
    await this.adjustSampleRate();
} catch (e) {
  console.error(Failed to get battery info. Code: {e.code}, message: {e.message});
}
public async startMonitoring(): Promise<boolean> {
try {
// 创建音频采集器
const audioCapturerConfig: audio.AudioCapturerConfig = {
audioStreamInfo: {
samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_44100,
channels: audio.AudioChannel.CHANNEL_1,
sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE,
encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW
},
capturerInfo: {
source: audio.SourceType.SOURCE_TYPE_MIC,
capturerFlags: 0
};
  this.audioCapturer = await audio.createAudioCapturer(audioCapturerConfig);
  await this.audioCapturer.start();
  
  // 启动音频数据采集循环
  this.startAudioCaptureLoop();
  
  return true;
catch (e) {
  console.error(Failed to start monitoring. Code: {e.code}, message: {e.message});
  return false;
}
private async restartAudioCapture(): Promise<void> {
if (this.audioCapturer) {
await this.audioCapturer.stop();
await this.audioCapturer.release();
this.audioCapturer = null;
await this.startMonitoring();
private async startAudioCaptureLoop(): Promise<void> {
if (!this.audioCapturer) return;
const bufferSize = await this.audioCapturer.getBufferSize();
const buffer = new ArrayBuffer(bufferSize);
while (this.audioCapturer) {
  try {
    const readResult = await this.audioCapturer.read(buffer, false);
    if (readResult === bufferSize) {
      const audioData = new Int16Array(buffer);
      this.processAudioData(audioData);
} catch (e) {
    console.error(Failed to read audio data. Code: {e.code}, message: {e.message});
    break;
// 根据采样率调整采集间隔
  await new Promise(resolve => setTimeout(resolve, 1000 / (this.deviceStatus.sampleRate / bufferSize * 2)));
}
private async processAudioData(audioData: Int16Array): Promise<void> {
// 1. 保存原始数据 (用于后续分析)
this.rawAudioData.push(…Array.from(audioData));
// 2. 当积累足够数据时进行处理 (约1秒数据)
if (this.rawAudioData.length >= this.deviceStatus.sampleRate) {
  // 使用任务池并行处理音频数据
  const task = new taskpool.Task(this.analyzeAudioDataTask, 
    this.rawAudioData.slice(0, this.deviceStatus.sampleRate),
    this.deviceStatus.sampleRate
  );
  
  const analysisResult = await taskpool.execute(task) as {
    dB: number, peakFreq: number, spectrum: number[]
  };
  
  // 3. 创建噪音数据记录
  const now = Date.now();
  const newData: NoiseData = {
    timestamp: now,
    dB: analysisResult.dB,
    frequencyPeak: analysisResult.peakFreq,
    spectrum: analysisResult.spectrum,
    deviceId: this.getDeviceId(),
    isSynced: false
  };
  
  this.noiseData.push(newData);
  
  // 4. 更新噪音等级记录
  this.updateNoiseLevel(newData.dB, now);
  
  // 5. 清理已处理的数据 (保留部分重叠)
  this.rawAudioData = this.rawAudioData.slice(this.deviceStatus.sampleRate / 2);
  
  // 6. 保存并同步数据
  await this.saveLocalData();
  
  if (now - this.deviceStatus.lastSyncTime > this.SYNC_INTERVAL) {
    await this.syncData();
    this.deviceStatus.lastSyncTime = now;
// 7. 根据噪音水平调整采样率
  await this.adjustSampleRate();
}
private analyzeAudioDataTask(audioData: number[], sampleRate: number): {
dB: number, peakFreq: number, spectrum: number[]
{
// 1. 计算分贝值 (简化版)
let sum = 0;
for (const sample of audioData) {
  sum += sample * sample;
const rms = Math.sqrt(sum / audioData.length);
const dB = 20 * Math.log10(rms / 32768); // 16位PCM最大值为32768
// 2. FFT频谱分析 (简化版)
const fftSize = 1024;
const spectrum = new Array(fftSize / 2).fill(0);
// 模拟FFT计算 (实际应用中应使用优化的FFT算法)
for (let i = 0; i < fftSize / 2; i++) {
  const freq = i * sampleRate / fftSize;
  if (freq < 20000) { // 人耳可听范围
    spectrum[i] = Math.random() * 10 + dB - 30; // 模拟频谱数据
}
// 3. 找出主要频率峰值
let peakFreq = 0;
let maxAmp = -Infinity;
for (let i = 1; i < spectrum.length - 1; i++) {
  if (spectrum[i] > maxAmp && spectrum[i] > spectrum[i - 1] && spectrum[i] > spectrum[i + 1]) {
    maxAmp = spectrum[i];
    peakFreq = i * sampleRate / fftSize;
}
return { dB, peakFreq, spectrum: spectrum.slice(0, 50) }; // 只返回前50个频点
private updateNoiseLevel(dB: number, timestamp: number): void {
const level = this.getNoiseLevel(dB);
// 检查是否与上一个记录相同等级
if (this.noiseLevels.length > 0) {
  const lastLevel = this.noiseLevels[this.noiseLevels.length - 1];
  if (lastLevel.level === level) {
    // 相同等级,更新持续时间
    lastLevel.duration = (timestamp - lastLevel.timestamp) / 1000;
    return;
}
// 添加新的噪音等级记录
const newLevel: NoiseLevel = {
  timestamp,
  level,
  duration: 0,
  isSynced: false
};
this.noiseLevels.push(newLevel);
private getNoiseLevel(dB: number): ‘quiet’ ‘moderate’ ‘loud’ ‘very_loud’
‘extreme’ {
if (dB < 40) return 'quiet';        // 安静: <40dB
if (dB < 60) return 'moderate';     // 中等: 40-60dB
if (dB < 80) return 'loud';         // 吵闹: 60-80dB
if (dB < 100) return 'very_loud';   // 非常吵闹: 80-100dB
return 'extreme';                    // 极端噪音: >100dB
private async triggerAlert(dB: number, timestamp: number): Promise<void> {
const level = this.getNoiseLevel(dB);
if (level = 'very_loud' || level = 'extreme') {
  const message = 噪音预警: {dB.toFixed(1)}dB ({level});
  
  try {
    await notification.publish({
      id: 1,
      contentType: notification.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
      normal: {
        title: '噪音污染预警',
        text: message,
        additionalText: new Date(timestamp).toLocaleTimeString()
});
catch (e) {
    console.error(Failed to publish notification. Code: {e.code}, message: {e.message});
// 通过分布式数据总线广播预警
  if (this.kvStore) {
    try {
      await this.kvStore.put('noise_alert', {
        value: {
          timestamp,
          dB,
          level,
          message,
          deviceId: this.getDeviceId()
});
catch (e) {
      console.error(Failed to sync alert. Code: {e.code}, message: {e.message});
}
}
private getDeviceId(): string {
// 实际应用中应获取真实设备ID
return ‘noise_sensor_’ + Math.random().toString(36).substr(2, 9);
private async syncData(): Promise<void> {
if (!this.kvStore) return;
try {
  // 同步噪音数据
  const unsyncedNoise = this.noiseData.filter(d => !d.isSynced);
  if (unsyncedNoise.length > 0) {
    await this.kvStore.put('noise_data', { value: unsyncedNoise });
    this.noiseData.forEach(d => {
      if (!d.isSynced) d.isSynced = true;
    });
// 同步噪音等级
  const unsyncedLevels = this.noiseLevels.filter(l => !l.isSynced);
  if (unsyncedLevels.length > 0) {
    await this.kvStore.put('noise_levels', { value: unsyncedLevels });
    this.noiseLevels.forEach(l => {
      if (!l.isSynced) l.isSynced = true;
    });
} catch (e) {
  console.error(Failed to sync data. Code: {e.code}, message: {e.message});
}
private handleRemoteDataChange(data: distributedData.ChangeData): void {
data.insertEntries.forEach((entry: distributedData.Entry) => {
if (entry.key === ‘noise_data’) {
const remoteData = entry.value.value as NoiseData[];
this.mergeNoiseData(remoteData);
else if (entry.key === ‘noise_levels’) {
    const remoteLevels = entry.value.value as NoiseLevel[];
    this.mergeNoiseLevels(remoteLevels);
else if (entry.key === ‘noise_alert’) {
    const remoteAlert = entry.value.value as {
      timestamp: number,
      dB: number,
      level: string,
      message: string,
      deviceId: string
    };
    
    this.handleRemoteAlert(remoteAlert);
});
private mergeNoiseData(remoteData: NoiseData[]): void {
remoteData.forEach(remote => {
  const existing = this.noiseData.find(local => 
    local.timestamp === remote.timestamp && 
    local.deviceId === remote.deviceId
  );
  
  if (!existing) {
    this.noiseData.push(remote);
else {
    // 合并策略:保留更高精度的数据
    if (remote.dB > existing.dB) {
      existing.dB = remote.dB;
      existing.frequencyPeak = remote.frequencyPeak;
      existing.spectrum = remote.spectrum;
}
});
// 按时间排序
this.noiseData.sort((a, b) => a.timestamp - b.timestamp);
private mergeNoiseLevels(remoteLevels: NoiseLevel[]): void {
remoteLevels.forEach(remote => {
  const existing = this.noiseLevels.find(local => 
    local.timestamp === remote.timestamp && 
    local.level === remote.level
  );
  
  if (!existing) {
    this.noiseLevels.push(remote);
else {
    // 合并持续时间
    existing.duration = Math.max(existing.duration, remote.duration);
});
// 按时间排序
this.noiseLevels.sort((a, b) => a.timestamp - b.timestamp);
private handleRemoteAlert(alert: {
timestamp: number,
dB: number,
level: string,
message: string,
deviceId: string
}): void {
// 避免处理自己的预警
if (alert.deviceId === this.getDeviceId()) return;
// 显示远程设备发来的预警
notification.publish({
  id: 2,
  contentType: notification.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
  normal: {
    title: 远程设备预警 (${alert.deviceId.substring(0, 6)}),
    text: alert.message,
    additionalText: new Date(alert.timestamp).toLocaleTimeString()
}).catch(e => {
  console.error(Failed to publish remote alert. Code: {e.code}, message: {e.message});
});
public getRecentNoiseData(count: number = 10): NoiseData[] {
return this.noiseData.slice(-count).reverse();
public getRecentNoiseLevels(count: number = 5): NoiseLevel[] {
return this.noiseLevels.slice(-count).reverse();
public getCurrentStatus(): DeviceStatus {
return this.deviceStatus;
public async stopMonitoring(): Promise<void> {
if (this.audioCapturer) {
  await this.audioCapturer.stop();
  await this.audioCapturer.release();
  this.audioCapturer = null;
await this.saveLocalData();
await this.syncData();
public async destroy(): Promise<void> {
await this.stopMonitoring();
if (this.kvStore) {
  this.kvStore.off('dataChange');
}
噪音监测组件实现
// src/main/ets/components/NoiseMonitor.ets
@Component
export struct NoiseMonitor {
private noiseService = NoiseMonitorService.getInstance();
@State noiseData: NoiseData[] = [];
@State noiseLevels: NoiseLevel[] = [];
@State deviceStatus: DeviceStatus = {
batteryLevel: 100,
isLowPowerMode: false,
sampleRate: 44100,
lastSyncTime: 0
};
@State isMonitoring: boolean = false;
private timer: number = 0;
aboutToAppear(): void {
this.loadData();
this.startMonitoring();
this.startAutoRefresh();
aboutToDisappear(): void {
this.stopAutoRefresh();
private loadData(): void {
this.noiseData = this.noiseService.getRecentNoiseData();
this.noiseLevels = this.noiseService.getRecentNoiseLevels();
this.deviceStatus = this.noiseService.getCurrentStatus();
private async startMonitoring(): Promise<void> {
this.isMonitoring = await this.noiseService.startMonitoring();
private startAutoRefresh(): void {
this.timer = setInterval(() => {
  this.loadData();
}, 10000); // 每10秒刷新一次
private stopAutoRefresh(): void {
if (this.timer) {
  clearInterval(this.timer);
  this.timer = 0;
}
build() {
Column() {
// 标题
Text(‘噪音污染监测’)
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 });
  // 状态卡片
  this.buildStatusCard();
  
  // 控制按钮
  Row() {
    Button(this.isMonitoring ? '停止监测' : '开始监测')
      .type(ButtonType.Capsule)
      .width('45%')
      .height(50)
      .backgroundColor(this.isMonitoring ? '#4CAF50' : '#F44336')
      .fontColor('#FFFFFF')
      .onClick(() => {
        this.toggleMonitoring();
      });
    
    Button('同步数据')
      .type(ButtonType.Capsule)
      .width('45%')
      .height(50)
      .backgroundColor('#2196F3')
      .fontColor('#FFFFFF')
      .margin({ left: 10 })
      .onClick(() => {
        this.syncData();
      });
.margin({ top: 20, bottom: 20 });
  // 实时噪音图表
  Text('实时噪音水平')
    .fontSize(18)
    .fontWeight(FontWeight.Bold)
    .margin({ bottom: 10 });
  
  this.buildNoiseChart();
  
  // 噪音等级记录
  Text('噪音等级记录')
    .fontSize(18)
    .fontWeight(FontWeight.Bold)
    .margin({ top: 20, bottom: 10 });
  
  if (this.noiseLevels.length > 0) {
    this.buildLevelHistory();
else {
    Text('暂无噪音等级记录')
      .fontSize(14)
      .fontColor('#666666');
}
.width('100%')
.height('100%')
.padding(20);
@Builder
private buildStatusCard() {
Column() {
// 当前噪音水平
Row() {
Image($r(‘app.media.ic_volume’))
.width(24)
.height(24)
.margin({ right: 10 });
    Column() {
      Text('当前噪音')
        .fontSize(14)
        .fontColor('#666666');
      
      if (this.noiseData.length > 0) {
        Text(${this.noiseData[0].dB.toFixed(1)} dB)
          .fontSize(24)
          .fontWeight(FontWeight.Bold)
          .fontColor(this.getNoiseColor(this.noiseData[0].dB));
        
        Text(主要频率: ${this.noiseData[0].frequencyPeak.toFixed(0)} Hz)
          .fontSize(14)
          .fontColor('#666666');
else {
        Text('-- dB')
          .fontSize(24)
          .fontWeight(FontWeight.Bold);
}
    .layoutWeight(1);
.margin({ bottom: 15 });
  // 设备状态
  Row() {
    Image($r('app.media.ic_settings'))
      .width(24)
      .height(24)
      .margin({ right: 10 });
    
    Column() {
      Text('设备状态')
        .fontSize(14)
        .fontColor('#666666');
      
      Row() {
        Text(电量: ${this.deviceStatus.batteryLevel}%)
          .fontSize(16);
        
        Text(this.deviceStatus.isLowPowerMode ? ' (低电量模式)' : '')
          .fontSize(16)
          .fontColor('#FF9800');
Text(采样率: ${this.deviceStatus.sampleRate / 1000} kHz)
        .fontSize(14)
        .fontColor('#666666')
        .margin({ top: 5 });
.layoutWeight(1);
}
.width('100%')
.padding(15)
.backgroundColor('#FFFFFF')
.borderRadius(10)
.shadow({ radius: 5, color: '#E0E0E0', offsetX: 0, offsetY: 2 });
private getNoiseColor(dB: number): string {
if (dB < 40) return '#4CAF50';    // 安静 - 绿色
if (dB < 60) return '#2196F3';     // 中等 - 蓝色
if (dB < 80) return '#FFC107';     // 吵闹 - 黄色
if (dB < 100) return '#FF9800';    // 非常吵闹 - 橙色
return '#F44336';                 // 极端噪音 - 红色
@Builder
private buildNoiseChart() {
// 获取最近1分钟数据 (每秒一个点)
const now = Date.now();
const oneMinuteAgo = now - 60 * 1000;
const recentData = this.noiseData.filter(d => d.timestamp >= oneMinuteAgo);
if (recentData.length === 0) {
  return Text('暂无噪音数据')
    .fontSize(14)
    .fontColor('#666666');
// 计算图表参数
const minDB = Math.max(20, Math.min(...recentData.map(d => d.dB)) - 10);
const maxDB = Math.min(120, Math.max(...recentData.map(d => d.dB)) + 10);
const dbRange = maxDB - minDB;
Column() {
  // 图表区域
  Stack() {
    // 网格线
    ForEach(Array.from({ length: 5 }), (_, i) => {
      Line()
        .width('100%')
        .height(1)
        .backgroundColor('#E0E0E0')
        .position({ x: 0, y: i * 40 });
    });
    
    // 噪音曲线
    Polyline()
      .width('100%')
      .height(160)
      .fillOpacity(0)
      .stroke('#E91E63')
      .strokeWidth(2)
      .points(this.getChartPoints(recentData, minDB, dbRange));
    
    // 数据点
    ForEach(recentData, (data, i) => {
      if (i % 5 === 0) { // 每5个数据显示一个点
        Circle()
          .width(8)
          .height(8)
          .fill('#E91E63')
          .position({
            x: i * (100 / (recentData.length - 1)) + '%',
            y: (maxDB - data.dB) * (160 / dbRange) + '%'
          });
});
.width(‘100%’)
  .height(160);
  
  // X轴标签
  Row() {
    Text(new Date(recentData[0].timestamp).toLocaleTimeString())
      .fontSize(10);
    
    Text(new Date(recentData[recentData.length - 1].timestamp).toLocaleTimeString())
      .fontSize(10)
      .margin({ left: '80%' });
.width(‘100%’)
  .margin({ top: 5 });
  
  // Y轴标签
  Row() {
    Text(${maxDB} dB)
      .fontSize(10);
    
    Text(${minDB} dB)
      .fontSize(10)
      .margin({ left: '85%' });
.width(‘100%’);
}
private getChartPoints(data: NoiseData[], minDB: number, dbRange: number): Point[] {
return data.map((d, i) => ({
x: i * (100 / (data.length - 1)),
y: (minDB + dbRange - d.dB) * (160 / dbRange)
}));
@Builder
private buildLevelHistory() {
List({ space: 10 }) {
ForEach(this.noiseLevels, (level) => {
ListItem() {
Column() {
Row() {
Text(this.getLevelText(level.level))
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor(this.getLevelColor(level.level))
.layoutWeight(1);
          Text(${level.duration.toFixed(0)}秒)
            .fontSize(14)
            .fontColor('#666666');
Text(new Date(level.timestamp).toLocaleTimeString())
          .fontSize(12)
          .fontColor('#666666')
          .margin({ top: 5 });
.width(‘100%’)
      .padding(10)
.borderRadius(10)
    .backgroundColor('#FFFFFF')
    .shadow({ radius: 3, color: '#E0E0E0', offsetX: 0, offsetY: 1 });
  })
.width(‘100%’)
.height('30%');
private getLevelText(level: string): string {
switch (level) {
  case 'quiet': return '安静';
  case 'moderate': return '中等';
  case 'loud': return '吵闹';
  case 'very_loud': return '非常吵闹';
  case 'extreme': return '极端噪音';
  default: return level;
}
private getLevelColor(level: string): string {
switch (level) {
case ‘quiet’: return ‘#4CAF50’;
case ‘moderate’: return ‘#2196F3’;
case ‘loud’: return ‘#FFC107’;
case ‘very_loud’: return ‘#FF9800’;
case ‘extreme’: return ‘#F44336’;
default: return ‘#9E9E9E’;
}
private async toggleMonitoring(): Promise<void> {
if (this.isMonitoring) {
await this.noiseService.stopMonitoring();
this.isMonitoring = false;
else {
  this.isMonitoring = await this.noiseService.startMonitoring();
}
private async syncData(): Promise<void> {
await this.noiseService.syncData();
prompt.showToast({ message: ‘数据同步中…’, duration: 2000 });
this.loadData();
}
主界面实现
// src/main/ets/pages/NoisePage.ets
import { NoiseMonitorService } from ‘…/service/NoiseMonitorService’;
import { NoiseMonitor } from ‘…/components/NoiseMonitor’;
@Entry
@Component
struct NoisePage {
@State activeTab: number = 0;
private noiseService = NoiseMonitorService.getInstance();
build() {
Column() {
// 标题
Text(‘环境噪音监测’)
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 });
  // 标签页
  Tabs({ barPosition: BarPosition.Start }) {
    TabContent() {
      // 实时监测标签页
      NoiseMonitor()
.tabBar(‘实时监测’);
    TabContent() {
      // 数据分析标签页
      this.buildAnalyticsTab()
.tabBar(‘数据分析’);
.barWidth(‘100%’)
  .barHeight(50)
  .width('100%')
  .height('80%')
.width(‘100%’)
.height('100%')
.padding(20);
@Builder
private buildAnalyticsTab() {
Column() {
Text(‘噪音统计分析’)
.fontSize(20)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 });
  // 今日噪音统计
  this.buildDailyStats();
  
  // 频谱分析
  Text('频谱特征分析')
    .fontSize(18)
    .fontWeight(FontWeight.Bold)
    .margin({ top: 30, bottom: 10 });
  
  this.buildSpectrumChart();
.width(‘100%’)
.height('100%')
.padding(20);
@Builder
private buildDailyStats() {
const now = Date.now();
const todayStart = new Date();
todayStart.setHours(0, 0, 0, 0);
const todayData = this.noiseService.getRecentNoiseData(1000).filter(d => 
  d.timestamp >= todayStart.getTime()
);
if (todayData.length === 0) {
  return Text('今日暂无噪音数据')
    .fontSize(14)
    .fontColor('#666666');
const avgDB = todayData.reduce((sum, d) => sum + d.dB, 0) / todayData.length;
const maxDB = Math.max(...todayData.map(d => d.dB));
const minDB = Math.min(...todayData.map(d => d.dB));
Column() {
  Row() {
    Column() {
      Text('平均噪音')
        .fontSize(14)
        .fontColor('#666666');
      
      Text(${avgDB.toFixed(1)} dB)
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .fontColor(this.getNoiseColor(avgDB));
.width(‘33%’)
    Column() {
      Text('最高噪音')
        .fontSize(14)
        .fontColor('#666666');
      
      Text(${maxDB.toFixed(1)} dB)
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .fontColor(this.getNoiseColor(maxDB));
.width(‘33%’)
    Column() {
      Text('最低噪音')
        .fontSize(14)
        .fontColor('#666666');
      
      Text(${minDB.toFixed(1)} dB)
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .fontColor(this.getNoiseColor(minDB));
.width(‘33%’)
.margin({ bottom: 20 });
  // 噪音等级分布
  Text('噪音等级分布')
    .fontSize(16)
    .fontWeight(FontWeight.Bold)
    .margin({ bottom: 10 });
  
  this.buildLevelDistribution();
.width(‘100%’)
.padding(15)
.backgroundColor('#FFFFFF')
.borderRadius(10)
.shadow({ radius: 5, color: '#E0E0E0', offsetX: 0, offsetY: 2 });
private getNoiseColor(dB: number): string {
if (dB < 40) return '#4CAF50';
if (dB < 60) return '#2196F3';
if (dB < 80) return '#FFC107';
if (dB < 100) return '#FF9800';
return '#F44336';
@Builder
private buildLevelDistribution() {
const todayLevels = this.noiseService.getRecentNoiseLevels(1000).filter(l =>
l.timestamp >= new Date().setHours(0, 0, 0, 0)
);
if (todayLevels.length === 0) {
  return Text('暂无噪音等级数据')
    .fontSize(14)
    .fontColor('#666666');
// 计算各等级持续时间
const levelStats: Record<string, number> = {
  quiet: 0,
  moderate: 0,
  loud: 0,
  very_loud: 0,
  extreme: 0
};
todayLevels.forEach(level => {
  levelStats[level.level] += level.duration;
});
// 计算总时间
const totalDuration = Object.values(levelStats).reduce((sum, val) => sum + val, 0);
if (totalDuration === 0) return;
// 构建饼图
Row() {
  // 图例
  Column() {
    ForEach(Object.entries(levelStats), ([level, duration]) => {
      if (duration > 0) {
        Row() {
          Circle()
            .width(12)
            .height(12)
            .fill(this.getLevelColor(level));
          
          Text({this.getLevelText(level)}: {(duration / 60).toFixed(1)}分钟)
            .fontSize(12)
            .margin({ left: 5 });
.margin({ bottom: 5 });
})
.width(‘40%’)
  // 饼图
  Stack() {
    let startAngle = 0;
    
    ForEach(Object.entries(levelStats), ([level, duration]) => {
      if (duration > 0) {
        const sweepAngle = 360 * duration / totalDuration;
        
        Circle()
          .width(120)
          .height(120)
          .fillOpacity(0)
          .stroke(this.getLevelColor(level))
          .strokeWidth(60)
          .startAngle(startAngle)
          .sweepAngle(sweepAngle);
        
        startAngle += sweepAngle;
})
.width(120)
  .height(120)
.width(‘100%’)
.justifyContent(FlexAlign.SpaceBetween)
@Builder
private buildSpectrumChart() {
const recentData = this.noiseService.getRecentNoiseData(1);
if (recentData.length = 0 |!recentData[0].spectrum
| recentData[0].spectrum.length = 0) {
return Text(‘暂无频谱数据’)
.fontSize(14)
.fontColor(‘#666666’);
const spectrum = recentData[0].spectrum;
const maxFreq = spectrum.length * 100; // 假设每个频点代表100Hz
Column() {
  // 频谱图
  Row({ space: 5 }) {
    ForEach(spectrum, (value, i) => {
      const freq = i * 100;
      const height = Math.min(100, Math.max(5, value * 2)); // 缩放显示
      
      Column() {
        Blank()
          .height(height)
          .width(10)
          .backgroundColor('#2196F3');
        
        if (i % 5 === 0) { // 每5个频点显示一个标签
          Text(${freq}Hz)
            .fontSize(8)
            .margin({ top: 5 });
}
    })
.height(120)
  .width('100%')
  .justifyContent(FlexAlign.End);
  
  // 频率轴
  Line()
    .width('100%')
    .height(1)
    .backgroundColor('#000000');
.width(‘100%’)
.height(150)
}
四、与游戏同步技术的结合点
实时数据同步:借鉴游戏中玩家状态实时同步机制,优化噪音数据的跨设备同步
事件广播机制:类似游戏中的事件广播,实现噪音预警的快速扩散
数据压缩传输:使用类似游戏中的网络优化技术,对频谱数据进行高效压缩传输
设备角色分配:参考游戏中的主机/客户端模式,确定主监测设备和从属设备
状态一致性保障:借鉴游戏中的状态同步机制,确保多设备间预警状态一致
五、关键特性实现
麦克风采样率自适应:
  private async adjustSampleRate(): Promise<void> {
 // 根据电池状态调整采样率
 await this.checkBatteryStatus();
 
 let newSampleRate = 44100; // 默认44.1kHz
 
 if (this.deviceStatus.isLowPowerMode) {
   newSampleRate = 16000; // 低电量模式使用16kHz
else {
   // 根据最近噪音水平动态调整
   if (this.noiseLevels.length > 0) {
     const lastLevel = this.noiseLevels[this.noiseLevels.length - 1];
     if (lastLevel.level = 'quiet' || lastLevel.level = 'moderate') {
       newSampleRate = 22050; // 安静环境降低采样率
else if (lastLevel.level === ‘extreme’) {
       newSampleRate = 48000; // 极端噪音提高采样率
}
if (newSampleRate !== this.deviceStatus.sampleRate) {
   this.deviceStatus.sampleRate = newSampleRate;
   await this.restartAudioCapture();
}
FFT频谱分析优化:
  private analyzeAudioDataTask(audioData: number[], sampleRate: number): {
 dB: number, peakFreq: number, spectrum: number[]
{
 // 1. 计算分贝值 (简化版)
 let sum = 0;
 for (const sample of audioData) {
   sum += sample * sample;
const rms = Math.sqrt(sum / audioData.length);
 const dB = 20 * Math.log10(rms / 32768); // 16位PCM最大值为32768
 
 // 2. FFT频谱分析 (简化版)
 const fftSize = 1024;
 const spectrum = new Array(fftSize / 2).fill(0);
 
 // 模拟FFT计算 (实际应用中应使用优化的FFT算法)
 for (let i = 0; i < fftSize / 2; i++) {
   const freq = i * sampleRate / fftSize;
   if (freq < 20000) { // 人耳可听范围
     spectrum[i] = Math.random() * 10 + dB - 30; // 模拟频谱数据
}
 // 3. 找出主要频率峰值
 let peakFreq = 0;
 let maxAmp = -Infinity;
 for (let i = 1; i < spectrum.length - 1; i++) {
   if (spectrum[i] > maxAmp && spectrum[i] > spectrum[i - 1] && spectrum[i] > spectrum[i + 1]) {
     maxAmp = spectrum[i];
     peakFreq = i * sampleRate / fftSize;
}
 return { dB, peakFreq, spectrum: spectrum.slice(0, 50) }; // 只返回前50个频点
数据分级存储:
  private updateNoiseLevel(dB: number, timestamp: number): void {
 const level = this.getNoiseLevel(dB);
 
 // 检查是否与上一个记录相同等级
 if (this.noiseLevels.length > 0) {
   const lastLevel = this.noiseLevels[this.noiseLevels.length - 1];
   if (lastLevel.level === level) {
     // 相同等级,更新持续时间
     lastLevel.duration = (timestamp - lastLevel.timestamp) / 1000;
     return;
}
 // 添加新的噪音等级记录
 const newLevel: NoiseLevel = {
   timestamp,
   level,
   duration: 0,
   isSynced: false
 };
 
 this.noiseLevels.push(newLevel);
分布式预警同步:
  private async triggerAlert(dB: number, timestamp: number): Promise<void> {
 const level = this.getNoiseLevel(dB);
 if (level = 'very_loud' || level = 'extreme') {
   const message = 噪音预警: {dB.toFixed(1)}dB ({level});
   
   try {
     await notification.publish({
       id: 1,
       contentType: notification.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
       normal: {
         title: '噪音污染预警',
         text: message,
         additionalText: new Date(timestamp).toLocaleTimeString()
});
catch (e) {
     console.error(Failed to publish notification. Code: {e.code}, message: {e.message});
// 通过分布式数据总线广播预警
   if (this.kvStore) {
     try {
       await this.kvStore.put('noise_alert', {
         value: {
           timestamp,
           dB,
           level,
           message,
           deviceId: this.getDeviceId()
});
catch (e) {
       console.error(Failed to sync alert. Code: {e.code}, message: {e.message});
}
}
六、性能优化策略
智能数据同步:
  // 只有新数据时才触发同步
if (now - this.deviceStatus.lastSyncTime > this.SYNC_INTERVAL) {
await this.syncData();
this.deviceStatus.lastSyncTime = now;
本地缓存优先:
  public getRecentNoiseData(count: number = 10): NoiseData[] {
 // 先从内存缓存读取
 return this.noiseData.slice(-count).reverse();
并行计算优化:
  // 使用任务池并行处理音频数据
const task = new taskpool.Task(this.analyzeAudioDataTask,
this.rawAudioData.slice(0, this.deviceStatus.sampleRate),
this.deviceStatus.sampleRate
);
const analysisResult = await taskpool.execute(task) as {
dB: number, peakFreq: number, spectrum: number[]
};
资源释放管理:
  public async destroy(): Promise<void> {
 await this.stopMonitoring();
 
 if (this.kvStore) {
   this.kvStore.off('dataChange');
}
七、项目扩展方向
地理围栏集成:结合GPS数据实现噪音污染地图
声音识别分类:增加AI模型识别不同类型的噪音源
长期趋势分析:提供噪音污染的长期趋势报告
智能报警阈值:根据时间和位置动态调整报警阈值
社区噪音排名:建立社区噪音水平排名系统
八、总结
本文实现的噪音污染监测系统具有以下特点:
采用自适应采样率技术,平衡监测精度和能耗
优化FFT频谱分析算法,精准识别噪音特征
实现数据分级存储策略,高效管理监测数据
基于分布式数据同步,支持多终端协同预警
提供直观的数据可视化和完整的噪音报告功能
该应用展示了HarmonyOS在环境监测领域的强大能力,特别是在音频处理、自适应控制和多设备协同方面的优势。通过借鉴游戏同步技术,实现了高效可靠的噪音预警机制,为城市噪音污染治理提供了完整的解决方案。




















