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
项目背景¶
市场需求¶
- 快速、卫生的体温测量需求
- 适合婴幼儿和老年人使用
- 疫情期间大规模筛查需求
- 简单易用的家用设备
技术挑战¶
- 测量准确性:确保红外测温准确可靠
- 环境补偿:补偿环境温度影响
- 用户体验:一键测量,快速显示
- 电源管理:延长电池使用寿命
- 合规性:满足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标准进行精度验证:
- 测试方法:黑体辐射源对比
- 测试温度点:35.0°C, 37.0°C, 39.0°C, 41.0°C
- 测试次数:每个温度点测量30次
- 验收标准:
- 平均误差 ≤ 0.2°C
- 标准差 ≤ 0.1°C
- 最大误差 ≤ 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. 风险和缓解措施
经验教训¶
成功经验¶
- 简化设计
- Class A软件允许简化的开发流程
- 减少文档负担,专注核心功能
-
快速迭代和验证
-
模块化架构
- 清晰的模块边界便于测试
- 硬件抽象层简化移植
-
易于维护和升级
-
早期原型
- 快速验证算法可行性
- 及早发现硬件问题
- 降低开发风险
遇到的挑战¶
- 环境干扰
- 问题:环境温度变化影响测量精度
-
解决:实现环境温度补偿算法,增加预热时间
-
用户操作
- 问题:用户测量距离不一致
-
解决:添加距离提示,优化UI引导
-
电池管理
- 问题:LCD显示耗电较大
- 解决:优化显示时间,实现快速睡眠
Class A开发特点¶
- 文档简化
- 不需要详细的软件架构文档
- 可以合并某些文档
-
测试文档可以简化
-
流程灵活
- 允许更灵活的开发流程
- 可以采用敏捷方法
-
减少审查和批准环节
-
风险管理
- 风险分析可以简化
- 重点关注已知风险
- 剩余风险评估相对简单
改进建议¶
- 增强功能
- 添加物体温度测量模式
- 支持蓝牙数据传输
-
增加趋势分析功能
-
用户体验
- 语音播报功能
- 彩色LCD显示
-
更直观的操作提示
-
质量提升
- 增加自校准功能
- 实现温度漂移补偿
- 提高长期稳定性
总结¶
本案例展示了Class A医疗器械软件的开发过程,包括:
- 基于风险的软件安全分类(Class A)
- 简化但完整的需求分析
- 模块化的架构设计
- 关键算法的实现(温度计算、环境补偿)
- 适度的测试策略
- 符合IEC 62304的基本文档要求
Class A软件的关键特点: - 软件故障不太可能导致严重伤害 - 允许简化的开发流程和文档 - 仍需要基本的质量管理和风险控制 - 适合快速开发和迭代
通过遵循IEC 62304标准的Class A要求,在保证安全性的同时,实现了高效的开发流程。
参考资料¶
- IEC 62304:2006+AMD1:2015 - Medical device software - Software life cycle processes
- ASTM E1965-98 - Standard Specification for Infrared Thermometers for Intermittent Determination of Patient Temperature
- ISO 14971:2019 - Medical devices - Application of risk management to medical devices
- ISO 80601-2-56:2017 - Medical electrical equipment - Part 2-56: Particular requirements for clinical thermometers for body temperature measurement
- FDA Guidance - Content of Premarket Submissions for Software Contained in Medical Devices
💬 讨论区
欢迎在这里分享您的想法、提出问题或参与讨论。需要 GitHub 账号登录。