跳转至

Class A案例:红外体温计

案例概述

本案例描述一个非接触式红外体温计的软件开发过程,展示如何应用IEC 62304标准开发Class A医疗器械软件。

设备信息: - 设备名称:非接触式红外体温计 - 预期用途:家庭和医疗环境下测量人体额温 - 测量范围:32.0°C - 42.9°C (89.6°F - 109.2°F) - 测量精度:±0.2°C (35.5°C - 42.0°C范围内) - 测量方法:红外热电堆传感器 - 监管分类:Class I(欧盟)/ Class II(美国FDA) - 软件安全分类:Class A

项目背景

市场需求

  • 快速、卫生的体温测量需求
  • 适合婴幼儿和老年人使用
  • 疫情期间大规模筛查需求
  • 简单易用的家用设备

技术挑战

  1. 测量准确性:确保红外测温准确可靠
  2. 环境补偿:补偿环境温度影响
  3. 用户体验:一键测量,快速显示
  4. 电源管理:延长电池使用寿命
  5. 合规性:满足IEC 62304和相关标准

软件安全分类

风险分析

根据ISO 14971进行风险分析:

危害 危害情况 后果 严重度 软件贡献
错误的体温读数 软件算法错误导致读数偏高 可能延误发热诊断 轻微 直接原因
错误的体温读数 软件算法错误导致读数偏低 可能导致不必要的担心 可忽略 直接原因
显示错误 软件显示错误的单位 用户误读温度值 可忽略 直接原因
数据丢失 存储模块故障 历史数据丢失 可忽略 直接原因

分类结果

软件安全等级:Class A

理由: - 软件故障不太可能导致严重伤害 - 体温计仅用于初步筛查,不用于诊断决策 - 异常体温通常会通过其他方式(如临床症状)验证 - 错误读数的后果是可接受的(延误诊断或不必要的担心) - 设备不直接控制治疗或生命支持功能

软件需求

系统需求

SR-001:体温测量 - 系统应测量人体额温 - 测量范围:32.0°C - 42.9°C - 测量精度:±0.2°C (35.5°C - 42.0°C范围内) - 测量时间:≤1秒

SR-002:用户界面 - 系统应提供LCD显示 - 支持一键测量操作 - 显示温度值和单位

SR-003:数据存储 - 系统应存储至少32次测量记录 - 记录包含温度值和时间戳

SR-004:电源管理 - 系统应使用2节AAA电池 - 电池寿命应支持至少1000次测量 - 自动关机功能(30秒无操作)

软件需求

SWR-001:温度采集

需求ID:SWR-001
描述:软件应从红外传感器读取温度数据
优先级:高
安全等级:Class A
验收标准:
1. 采样率:10 Hz
2. 采样时间:1秒
3. 读取传感器原始数据
4. 检测传感器故障
追溯:SR-001

SWR-002:温度计算

需求ID:SWR-002
描述:软件应计算体温值
优先级:高
安全等级:Class A
验收标准:
1. 环境温度补偿
2. 距离补偿(假设测量距离3-5cm)
3. 多次采样平均
4. 精度:±0.2°C
追溯:SR-001

SWR-003:数据存储

需求ID:SWR-003
描述:软件应存储测量数据
优先级:中
安全等级:Class A
验收标准:
1. 存储至少32条记录
2. 每条记录包含:温度值、时间戳
3. 存储器满时覆盖最旧记录
追溯:SR-003

SWR-004:用户界面

需求ID:SWR-004
描述:软件应提供LCD显示和按键输入
优先级:中
安全等级:Class A
验收标准:
1. 显示温度值(一位小数)
2. 显示单位(°C或°F)
3. 显示电池电量
4. 支持单位切换
追溯:SR-002

SWR-005:电源管理

需求ID:SWR-005
描述:软件应实现电源管理
优先级:中
安全等级:Class A
验收标准:
1. 30秒无操作自动关机
2. 低电量警告
3. 睡眠模式功耗 < 10μA
追溯:SR-004

说明: 此需求定义了电源管理功能,包括30秒无操作自动关机、低电量警告和睡眠模式功耗要求。这些是Class A设备的典型电源管理需求,确保设备在无操作时节能,并在电量不足时及时提醒用户。

软件架构设计

系统架构

┌─────────────────────────────────────────┐
│           应用层 (Application)           │
├─────────────────────────────────────────┤
│  测量控制  │  温度计算  │  数据管理  │  UI │
├─────────────────────────────────────────┤
│         硬件抽象层 (HAL)                 │
├─────────────────────────────────────────┤
│  I2C  │  GPIO  │  LCD  │  EEPROM  │  RTC │
└─────────────────────────────────────────┘

说明: 此架构图展示了体温计软件的分层设计。应用层包含测量控制、温度计算、数据管理和用户界面模块;硬件抽象层(HAL)封装了I2C、GPIO、LCD、EEPROM和RTC等硬件接口,实现了软件与硬件的解耦,便于测试和维护。

模块设计

1. 测量控制模块(Measurement Control)

/**
 * @module MeasurementControl
 * @trace SWR-001
 * @description 控制体温测量过程
 */

typedef enum {
    TEMP_STATE_IDLE,
    TEMP_STATE_MEASURING,
    TEMP_STATE_COMPLETE,
    TEMP_STATE_ERROR
} temp_state_t;

typedef struct {
    float temperature;      // 体温值(°C)
    uint32_t timestamp;     // 时间戳
    bool valid;             // 数据有效性
} temp_result_t;

// 初始化测量模块
void temp_measurement_init(void);

// 开始测量
bool temp_measurement_start(void);

// 获取测量结果
bool temp_measurement_get_result(temp_result_t* result);

// 获取测量状态
temp_state_t temp_measurement_get_state(void);

2. 温度计算模块(Temperature Calculation)

/**
 * @module TemperatureCalculation
 * @trace SWR-002
 * @description 计算体温值
 */

#define SAMPLE_COUNT 10

typedef struct {
    float object_temp;      // 物体温度
    float ambient_temp;     // 环境温度
    float distance;         // 测量距离(cm)
} temp_raw_data_t;

// 初始化温度计算模块
void temp_calc_init(void);

// 添加采样数据
void temp_calc_add_sample(const temp_raw_data_t* data);

// 计算体温
bool temp_calc_calculate(float* temperature);

// 重置计算
void temp_calc_reset(void);

3. 数据管理模块(Data Management)

/**
 * @module DataManagement
 * @trace SWR-003
 * @description 管理测量数据存储
 */

#define MAX_TEMP_RECORDS 32

typedef struct {
    float temperature;
    uint32_t timestamp;
} temp_record_t;

// 初始化数据管理
void temp_data_init(void);

// 保存测量记录
bool temp_data_save(float temperature);

// 读取记录
bool temp_data_read(uint16_t index, temp_record_t* record);

// 获取记录数量
uint16_t temp_data_get_count(void);

// 清除所有数据
void temp_data_clear(void);

关键实现

温度计算算法实现

/**
 * @file temp_calculation.c
 * @trace SWR-002
 * @description 体温计算算法实现
 */

#include <stdint.h>
#include <stdbool.h>
#include <math.h>
#include "temp_calculation.h"

static temp_raw_data_t g_samples[SAMPLE_COUNT];
static uint8_t g_sample_count = 0;

void temp_calc_init(void) {
    g_sample_count = 0;
}

void temp_calc_add_sample(const temp_raw_data_t* data) {
    if (g_sample_count < SAMPLE_COUNT) {
        g_samples[g_sample_count] = *data;
        g_sample_count++;
    }
}

/**
 * @brief 环境温度补偿
 * @trace SWR-002: 环境温度补偿
 * @description 根据环境温度调整物体温度读数
 */
static float compensate_ambient(float object_temp, float ambient_temp) {
    // 简化的补偿算法
    // 实际应用中需要根据传感器特性校准
    float temp_diff = object_temp - ambient_temp;
    float compensation = temp_diff * 0.05f;  // 5%补偿系数

    return object_temp + compensation;
}

/**
 * @brief 距离补偿
 * @trace SWR-002: 距离补偿
 * @description 根据测量距离调整温度读数
 */
static float compensate_distance(float temperature, float distance) {
    // 假设标准距离为5cm
    const float STANDARD_DISTANCE = 5.0f;

    if (distance < 3.0f || distance > 7.0f) {
        return temperature;  // 距离超出范围,不补偿
    }

    // 距离每偏离1cm,温度偏差约0.1°C
    float distance_error = (distance - STANDARD_DISTANCE) * 0.1f;

    return temperature - distance_error;
}

/**
 * @brief 计算平均温度
 * @trace SWR-002: 多次采样平均
 */
static float calculate_average(void) {
    float sum = 0.0f;

    for (uint8_t i = 0; i < g_sample_count; i++) {
        float temp = g_samples[i].object_temp;

        // 应用补偿
        temp = compensate_ambient(temp, g_samples[i].ambient_temp);
        temp = compensate_distance(temp, g_samples[i].distance);

        sum += temp;
    }

    return sum / g_sample_count;
}

/**
 * @brief 异常值检测
 * @description 检测并移除异常采样值
 */
static bool is_outlier(float value, float mean, float std_dev) {
    // 使用3-sigma规则检测异常值
    return fabs(value - mean) > (3.0f * std_dev);
}

bool temp_calc_calculate(float* temperature) {
    if (g_sample_count < SAMPLE_COUNT) {
        return false;  // 采样不足
    }

    // 计算初步平均值
    float mean = calculate_average();

    // 计算标准差
    float variance = 0.0f;
    for (uint8_t i = 0; i < g_sample_count; i++) {
        float temp = g_samples[i].object_temp;
        temp = compensate_ambient(temp, g_samples[i].ambient_temp);
        temp = compensate_distance(temp, g_samples[i].distance);

        float diff = temp - mean;
        variance += diff * diff;
    }
    float std_dev = sqrtf(variance / g_sample_count);

    // 移除异常值后重新计算
    float sum = 0.0f;
    uint8_t valid_count = 0;

    for (uint8_t i = 0; i < g_sample_count; i++) {
        float temp = g_samples[i].object_temp;
        temp = compensate_ambient(temp, g_samples[i].ambient_temp);
        temp = compensate_distance(temp, g_samples[i].distance);

        if (!is_outlier(temp, mean, std_dev)) {
            sum += temp;
            valid_count++;
        }
    }

    if (valid_count < SAMPLE_COUNT / 2) {
        return false;  // 有效采样太少
    }

    *temperature = sum / valid_count;

    // 验证温度范围
    if (*temperature < 32.0f || *temperature > 42.9f) {
        return false;  // 超出测量范围
    }

    return true;
}

void temp_calc_reset(void) {
    g_sample_count = 0;
}

代码说明: - compensate_ambient(): 补偿环境温度对测量的影响 - compensate_distance(): 补偿测量距离对精度的影响 - calculate_average(): 计算多次采样的平均值 - is_outlier(): 使用3-sigma规则检测异常值 - temp_calc_calculate(): 主计算函数,应用所有补偿并验证结果

数据存储实现

/**
 * @file temp_data.c
 * @trace SWR-003
 * @description 测量数据存储管理
 */

#include <stdint.h>
#include <stdbool.h>
#include "temp_data.h"
#include "eeprom.h"
#include "rtc.h"

#define EEPROM_BASE_ADDR 0x0000
#define RECORD_SIZE sizeof(temp_record_t)
#define INDEX_ADDR EEPROM_BASE_ADDR

static uint16_t g_record_index = 0;

void temp_data_init(void) {
    // 读取记录索引
    eeprom_read(INDEX_ADDR, (uint8_t*)&g_record_index, 2);

    // 验证索引有效性
    if (g_record_index >= MAX_TEMP_RECORDS) {
        g_record_index = 0;
    }
}

bool temp_data_save(float temperature) {
    // 准备记录
    temp_record_t record;
    record.temperature = temperature;
    record.timestamp = rtc_get_timestamp();

    // 计算存储地址
    uint16_t addr = EEPROM_BASE_ADDR + 2 + (g_record_index * RECORD_SIZE);

    // 写入EEPROM
    if (!eeprom_write(addr, (const uint8_t*)&record, RECORD_SIZE)) {
        return false;
    }

    // 更新索引(循环覆盖)
    g_record_index = (g_record_index + 1) % MAX_TEMP_RECORDS;
    eeprom_write(INDEX_ADDR, (const uint8_t*)&g_record_index, 2);

    return true;
}

bool temp_data_read(uint16_t index, temp_record_t* record) {
    if (index >= MAX_TEMP_RECORDS) {
        return false;
    }

    // 计算地址
    uint16_t addr = EEPROM_BASE_ADDR + 2 + (index * RECORD_SIZE);

    // 读取记录
    return eeprom_read(addr, (uint8_t*)record, RECORD_SIZE);
}

uint16_t temp_data_get_count(void) {
    return g_record_index;
}

void temp_data_clear(void) {
    g_record_index = 0;
    eeprom_write(INDEX_ADDR, (const uint8_t*)&g_record_index, 2);
}

代码说明: - temp_data_init(): 从EEPROM读取当前记录索引 - temp_data_save(): 保存新的测量记录,循环覆盖最旧记录 - temp_data_read(): 读取指定索引的历史记录 - temp_data_get_count(): 返回当前记录数量 - temp_data_clear(): 清除所有历史记录

电源管理实现

/**
 * @file power_management.c
 * @trace SWR-005
 * @description 电源管理实现
 */

#include <stdint.h>
#include <stdbool.h>
#include "power_management.h"
#include "system.h"

#define AUTO_OFF_TIMEOUT_MS 30000  // 30秒
#define LOW_BATTERY_THRESHOLD 2.4f  // 2.4V

static uint32_t g_last_activity_time = 0;
static bool g_low_battery_warning = false;

void power_init(void) {
    g_last_activity_time = system_get_tick();
    g_low_battery_warning = false;
}

void power_update_activity(void) {
    g_last_activity_time = system_get_tick();
}

/**
 * @brief 检查是否需要自动关机
 * @trace SWR-005: 30秒无操作自动关机
 */
bool power_should_shutdown(void) {
    uint32_t current_time = system_get_tick();
    uint32_t elapsed = current_time - g_last_activity_time;

    return (elapsed >= AUTO_OFF_TIMEOUT_MS);
}

/**
 * @brief 检查电池电量
 * @trace SWR-005: 低电量警告
 */
bool power_check_battery(void) {
    float battery_voltage = adc_read_battery_voltage();

    if (battery_voltage < LOW_BATTERY_THRESHOLD) {
        g_low_battery_warning = true;
        return false;
    }

    g_low_battery_warning = false;
    return true;
}

bool power_is_low_battery(void) {
    return g_low_battery_warning;
}

/**
 * @brief 进入睡眠模式
 * @trace SWR-005: 睡眠模式功耗 < 10μA
 */
void power_enter_sleep(void) {
    // 关闭外设
    lcd_power_off();
    sensor_power_off();

    // 配置唤醒源(按键中断)
    gpio_enable_wakeup(BUTTON_PIN);

    // 进入深度睡眠
    system_enter_deep_sleep();
}

void power_shutdown(void) {
    // 保存状态
    temp_data_save_state();

    // 显示关机信息
    lcd_show_message("OFF");
    system_delay_ms(1000);

    // 关闭所有外设
    lcd_power_off();
    sensor_power_off();

    // 进入最低功耗模式
    system_shutdown();
}

代码说明: - power_update_activity(): 更新最后活动时间,重置自动关机计时器 - power_should_shutdown(): 检查是否超过30秒无操作 - power_check_battery(): 检查电池电压,低于阈值时设置警告标志 - power_enter_sleep(): 进入低功耗睡眠模式,配置按键唤醒 - power_shutdown(): 完全关机,保存状态并关闭所有外设

测试策略

单元测试

测试用例TC-001:温度计算测试

/**
 * @test TC-001
 * @trace SWR-002
 * @description 测试温度计算算法的准确性
 */
void test_temperature_calculation(void) {
    temp_calc_init();

    // 准备测试数据(模拟37.0°C体温)
    temp_raw_data_t sample = {
        .object_temp = 36.8f,
        .ambient_temp = 25.0f,
        .distance = 5.0f
    };

    // 添加10个采样
    for (uint8_t i = 0; i < SAMPLE_COUNT; i++) {
        // 添加小的随机变化
        sample.object_temp = 36.8f + (i % 3) * 0.05f;
        temp_calc_add_sample(&sample);
    }

    // 计算温度
    float temperature;
    bool success = temp_calc_calculate(&temperature);

    // 验证结果
    assert(success == true);
    assert(temperature >= 36.8f && temperature <= 37.2f);
    assert(fabs(temperature - 37.0f) <= 0.2f);  // ±0.2°C精度
}

测试用例TC-002:数据存储测试

/**
 * @test TC-002
 * @trace SWR-003
 * @description 测试数据存储和读取
 */
void test_data_storage(void) {
    // 初始化
    temp_data_init();
    temp_data_clear();

    // 保存测试数据
    float test_temps[] = {36.5f, 37.0f, 37.5f, 38.0f};

    for (uint8_t i = 0; i < 4; i++) {
        bool success = temp_data_save(test_temps[i]);
        assert(success == true);
    }

    // 验证记录数量
    uint16_t count = temp_data_get_count();
    assert(count == 4);

    // 读取并验证数据
    for (uint8_t i = 0; i < 4; i++) {
        temp_record_t record;
        bool success = temp_data_read(i, &record);
        assert(success == true);
        assert(fabs(record.temperature - test_temps[i]) < 0.01f);
    }
}

测试用例TC-003:电源管理测试

/**
 * @test TC-003
 * @trace SWR-005
 * @description 测试自动关机功能
 */
void test_auto_shutdown(void) {
    // 初始化
    power_init();

    // 模拟用户活动
    power_update_activity();

    // 检查不应该关机
    assert(power_should_shutdown() == false);

    // 模拟30秒过去
    system_advance_time(30000);

    // 现在应该关机
    assert(power_should_shutdown() == true);

    // 更新活动后重置
    power_update_activity();
    assert(power_should_shutdown() == false);
}

集成测试

测试用例TC-010:完整测量流程

/**
 * @test TC-010
 * @trace SWR-001, SWR-002, SWR-003
 * @description 测试完整的测量流程
 */
void test_complete_measurement(void) {
    // 1. 初始化系统
    system_init();

    // 2. 开始测量
    bool start_success = temp_measurement_start();
    assert(start_success == true);

    // 3. 等待测量完成
    while (temp_measurement_get_state() != TEMP_STATE_COMPLETE) {
        system_delay_ms(100);
    }

    // 4. 获取结果
    temp_result_t result;
    bool result_success = temp_measurement_get_result(&result);
    assert(result_success == true);
    assert(result.valid == true);
    assert(result.temperature >= 32.0f && result.temperature <= 42.9f);

    // 5. 验证数据已保存
    uint16_t count = temp_data_get_count();
    assert(count > 0);
}

系统测试

测试用例TC-020:精度验证

根据ASTM E1965标准进行精度验证:

  1. 测试方法:黑体辐射源对比
  2. 测试温度点:35.0°C, 37.0°C, 39.0°C, 41.0°C
  3. 测试次数:每个温度点测量30次
  4. 验收标准
  5. 平均误差 ≤ 0.2°C
  6. 标准差 ≤ 0.1°C
  7. 最大误差 ≤ 0.3°C

测试用例TC-021:环境适应性测试

测试不同环境条件下的性能:

环境温度 相对湿度 测试结果
16°C 30% 通过
25°C 60% 通过
35°C 85% 通过

测试用例TC-022:电池寿命测试

  • 新电池连续测量1000次
  • 验证电池电压仍在工作范围内
  • 验证低电量警告功能

验证和确认

软件验证

  • ✓ 所有单元测试通过(覆盖率 > 90%)
  • ✓ 所有集成测试通过
  • ✓ 代码审查完成(无严重缺陷)
  • ✓ 静态分析通过(MISRA C合规)
  • ✓ 需求追溯完整(100%覆盖)

软件确认

  • ✓ 系统测试通过
  • ✓ 精度验证通过(符合ASTM E1965)
  • ✓ 环境适应性测试通过
  • ✓ 电池寿命测试通过
  • ✓ 用户可用性测试通过

风险管理

剩余风险评估

风险 控制措施 剩余风险等级 可接受性
错误读数 算法验证、精度测试 可接受
传感器故障 故障检测、错误提示 可接受
电池耗尽 低电量警告、自动关机 可忽略 可接受

所有剩余风险均在可接受范围内。

文档模板

软件需求规格说明(SRS)模板

# 软件需求规格说明

## 1. 引言
### 1.1 目的
### 1.2 范围
### 1.3 定义和缩略语

## 2. 总体描述
### 2.1 产品功能
### 2.2 用户特征
### 2.3 约束条件

## 3. 具体需求
### 3.1 功能需求
#### 3.1.1 需求ID: SWR-001
- 描述:
- 优先级:
- 安全等级:
- 验收标准:
- 追溯:

### 3.2 性能需求
### 3.3 接口需求
### 3.4 安全需求

## 4. 需求追溯矩阵

软件设计规格说明(SDS)模板

# 软件设计规格说明

## 1. 架构设计
### 1.1 系统架构图
### 1.2 模块划分
### 1.3 接口定义

## 2. 详细设计
### 2.1 模块A
#### 2.1.1 功能描述
#### 2.1.2 接口定义
#### 2.1.3 数据结构
#### 2.1.4 算法描述

## 3. 数据库设计
## 4. 用户界面设计
## 5. 设计追溯矩阵

软件测试计划(STP)模板

# 软件测试计划

## 1. 测试范围
## 2. 测试策略
### 2.1 单元测试
### 2.2 集成测试
### 2.3 系统测试

## 3. 测试用例
### TC-001: 测试用例名称
- 追溯:SWR-XXX
- 前置条件:
- 测试步骤:
- 预期结果:
- 实际结果:
- 状态:通过/失败

## 4. 测试环境
## 5. 测试进度
## 6. 风险和缓解措施

经验教训

成功经验

  1. 简化设计
  2. Class A软件允许简化的开发流程
  3. 减少文档负担,专注核心功能
  4. 快速迭代和验证

  5. 模块化架构

  6. 清晰的模块边界便于测试
  7. 硬件抽象层简化移植
  8. 易于维护和升级

  9. 早期原型

  10. 快速验证算法可行性
  11. 及早发现硬件问题
  12. 降低开发风险

遇到的挑战

  1. 环境干扰
  2. 问题:环境温度变化影响测量精度
  3. 解决:实现环境温度补偿算法,增加预热时间

  4. 用户操作

  5. 问题:用户测量距离不一致
  6. 解决:添加距离提示,优化UI引导

  7. 电池管理

  8. 问题:LCD显示耗电较大
  9. 解决:优化显示时间,实现快速睡眠

Class A开发特点

  1. 文档简化
  2. 不需要详细的软件架构文档
  3. 可以合并某些文档
  4. 测试文档可以简化

  5. 流程灵活

  6. 允许更灵活的开发流程
  7. 可以采用敏捷方法
  8. 减少审查和批准环节

  9. 风险管理

  10. 风险分析可以简化
  11. 重点关注已知风险
  12. 剩余风险评估相对简单

改进建议

  1. 增强功能
  2. 添加物体温度测量模式
  3. 支持蓝牙数据传输
  4. 增加趋势分析功能

  5. 用户体验

  6. 语音播报功能
  7. 彩色LCD显示
  8. 更直观的操作提示

  9. 质量提升

  10. 增加自校准功能
  11. 实现温度漂移补偿
  12. 提高长期稳定性

总结

本案例展示了Class A医疗器械软件的开发过程,包括:

  • 基于风险的软件安全分类(Class A)
  • 简化但完整的需求分析
  • 模块化的架构设计
  • 关键算法的实现(温度计算、环境补偿)
  • 适度的测试策略
  • 符合IEC 62304的基本文档要求

Class A软件的关键特点: - 软件故障不太可能导致严重伤害 - 允许简化的开发流程和文档 - 仍需要基本的质量管理和风险控制 - 适合快速开发和迭代

通过遵循IEC 62304标准的Class A要求,在保证安全性的同时,实现了高效的开发流程。

参考资料

  1. IEC 62304:2006+AMD1:2015 - Medical device software - Software life cycle processes
  2. ASTM E1965-98 - Standard Specification for Infrared Thermometers for Intermittent Determination of Patient Temperature
  3. ISO 14971:2019 - Medical devices - Application of risk management to medical devices
  4. ISO 80601-2-56:2017 - Medical electrical equipment - Part 2-56: Particular requirements for clinical thermometers for body temperature measurement
  5. FDA Guidance - Content of Premarket Submissions for Software Contained in Medical Devices

💬 讨论区

欢迎在这里分享您的想法、提出问题或参与讨论。需要 GitHub 账号登录。