第四章:测试软件框架

做ToF模组量产,硬件只是骨架,软件才是灵魂。我见过太多团队,硬件设计得漂漂亮亮,一到测试软件就翻车。说白了,测试软件框架决定了你的产线能跑多快、多稳。

这一章,我重点聊聊上位机软件架构、固件烧录与校准流程,还有自动化测试脚本怎么设计。嗯,都是我在产线上摸爬滚打总结出来的经验。

4.1 上位机软件架构:C# vs Python

先说说上位机。我个人习惯把上位机分成三层:通信层、业务逻辑层、UI层。别觉得复杂,这层架构能帮你省掉后期80%的维护成本。

核心原则:通信层只管收发数据,业务逻辑层只管处理数据,UI层只管展示数据。各干各的,互不干扰。

4.1.1 C#方案(推荐产线使用)

如果你问我产线用什么,我首选C#。为什么?因为C#的Windows Forms或WPF做UI太成熟了,产线工人操作起来直观。我在项目中遇到过,用Python写的界面,工人点错一个按钮就卡死,换成C#后稳定多了。

C#架构示例:

// 通信层 - 串口通信类
public class SerialPortManager
{
    private SerialPort _port;
    
    public bool Connect(string portName, int baudRate = 115200)
    {
        // 我习惯用115200,产线稳定
        _port = new SerialPort(portName, baudRate);
        _port.DataReceived += OnDataReceived;
        return _port.IsOpen;
    }
    
    private void OnDataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        // 异步处理,别阻塞UI
        byte[] buffer = new byte[_port.BytesToRead];
        _port.Read(buffer, 0, buffer.Length);
        // 触发业务层事件
        OnRawDataReceived?.Invoke(buffer);
    }
}

// 业务逻辑层 - 测试流程控制
public class TestController
{
    public TestResult RunCalibration(SerialPortManager com)
    {
        // 发送校准指令
        com.Send("CAL_START");
        // 等待响应
        var response = WaitForResponse(5000); // 5秒超时
        return ParseResult(response);
    }
}

我的小技巧:产线环境复杂,串口经常被占用。我习惯在连接前先枚举所有可用串口,让工人自己选,而不是写死COM口。

4.1.2 Python方案(适合研发验证)

Python呢?我一般在研发阶段用。快速验证算法、调试参数,Python的灵活性无可替代。但产线我不推荐,GIL锁和打包问题太头疼。

Python架构示例:

# 通信层 - 使用pyserial
import serial
import threading

class ToFComm:
    def __init__(self, port, baudrate=115200):
        self.ser = serial.Serial(port, baudrate, timeout=1)
        self._lock = threading.Lock()  # 线程安全
        
    def send_command(self, cmd):
        with self._lock:
            self.ser.write(cmd.encode())
            return self.ser.readline()
    
    def read_depth_data(self):
        """读取深度数据,返回16位数组"""
        raw = self.ser.read(1024)
        # 解析协议头
        if raw[0:2] != b'\xAA\x55':
            return None
        # 提取有效数据
        return [int.from_bytes(raw[i:i+2], 'little') 
                for i in range(4, len(raw), 2)]

# 业务逻辑层
class CalibrationFlow:
    def __init__(self, comm):
        self.comm = comm
        
    def run_offset_cal(self):
        """偏移校准 - 我曾经踩过坑,忘了清空缓存"""
        self.comm.ser.reset_input_buffer()  # 清空缓存,很重要!
        resp = self.comm.send_command("CAL_OFFSET")
        return resp == "OK"

注意:Python做产线测试时,记得用pyinstaller打包成exe。但打包后串口权限可能丢失,我建议用管理员权限运行。

4.2 固件烧录与校准流程

固件烧录,说白了就是把编译好的二进制文件写进ToF模组的Flash里。校准呢?就是让模组知道「什么是0,什么是1」。

我画了一张流程图,帮你理清整个流程:

固件烧录与校准流程 步骤1:上电检测 步骤2:固件烧录 步骤3:校验固件 校验失败 重新烧录 校验成功 步骤4:偏移校准 步骤5:增益校准

4.2.1 固件烧录细节

烧录方式我常用两种:

烧录方式 适用场景 速度 可靠性
SWD/JTAG 研发调试、小批量 快(~1MB/s) 极高
UART Bootloader 产线量产 中(~115200bps)
USB DFU 支持USB的模组 快(~12Mbps)

避坑指南:我曾经在产线上遇到批量烧录失败,查了三天发现是烧录器供电不稳。后来我强制要求:烧录时模组必须独立供电,不能靠烧录器供电。

4.2.2 校准流程详解

校准分两步:偏移校准和增益校准。你想想看,ToF模组出厂时每个像素的响应都不一样,不校准的话测出来的距离全是歪的。

偏移校准:让模组对着标准白板(距离固定,比如1米),记录每个像素的原始值,算出偏移量。

增益校准:换个距离(比如2米),再测一次,算出增益系数。

代码示例(Python):

def calibrate_tof_module(comm, distance_mm):
    """
    校准ToF模组
    :param comm: 通信对象
    :param distance_mm: 标准距离(毫米)
    """
    # 步骤1:设置模组为校准模式
    comm.send_command("ENTER_CAL_MODE")
    time.sleep(0.1)
    
    # 步骤2:采集100帧数据取平均
    frames = []
    for i in range(100):
        frame = comm.read_depth_data()
        if frame:
            frames.append(frame)
    
    avg_frame = np.mean(frames, axis=0)
    
    # 步骤3:计算偏移量
    # 理想值 = 实际距离 / 像素分辨率
    ideal_value = distance_mm / 0.1  # 假设分辨率0.1mm
    offset = ideal_value - avg_frame
    
    # 步骤4:写入校准参数
    comm.send_command(f"SET_OFFSET:{','.join(map(str, offset))}")
    
    # 步骤5:验证
    verify = comm.send_command("GET_OFFSET")
    if verify == "OK":
        print("校准成功")
    else:
        print("校准失败,请检查")

4.3 自动化测试脚本设计

产线测试,核心就一个字:快。你想想看,一条产线一天要测几千个模组,每个模组多花1秒,一天就多花1小时。所以自动化脚本的设计原则是:能并行就别串行,能异步就别同步。

4.3.1 测试脚本架构

我习惯把测试脚本分成三层:

  1. 测试用例层:每个用例测一个功能点(比如测距精度、帧率、温度漂移)
  2. 测试流程层:组合多个用例,形成完整的测试序列
  3. 报告生成层:把测试结果汇总成报表,支持MES系统对接

代码示例(C#自动化测试框架):

// 测试用例基类
public abstract class TestCase
{
    public string Name { get; set; }
    public bool IsPass { get; protected set; }
    public string ErrorMessage { get; protected set; }
    
    public abstract void Execute(ToFModule module);
}

// 具体测试用例:测距精度测试
public class DistanceAccuracyTest : TestCase
{
    private int _targetDistance; // 标准距离
    
    public DistanceAccuracyTest(int targetDistance)
    {
        Name = $"测距精度测试@{targetDistance}mm";
        _targetDistance = targetDistance;
    }
    
    public override void Execute(ToFModule module)
    {
        // 设置模组到目标距离
        module.SetTargetDistance(_targetDistance);
        
        // 采集10次取平均
        double sum = 0;
        for (int i = 0; i < 10; i++)
        {
            sum += module.ReadDistance();
        }
        double avg = sum / 10;
        
        // 判断误差是否在±1%以内
        double error = Math.Abs(avg - _targetDistance) / _targetDistance;
        IsPass = error <= 0.01;
        
        if (!IsPass)
            ErrorMessage = $"误差{error:P2},超过1%";
    }
}

// 测试流程管理
public class TestRunner
{
    private List<TestCase> _cases = new List<TestCase>();
    
    public void AddCase(TestCase testCase)
    {
        _cases.Add(testCase);
    }
    
    public TestReport RunAll(ToFModule module)
    {
        var report = new TestReport();
        
        foreach (var testCase in _cases)
        {
            try
            {
                testCase.Execute(module);
                report.AddResult(testCase.Name, testCase.IsPass);
            }
            catch (Exception ex)
            {
                report.AddResult(testCase.Name, false, ex.Message);
            }
        }
        
        return report;
    }
}

我的经验:产线测试脚本一定要加「看门狗」机制。如果某个测试用例卡住超过30秒,自动重启模组并重试。我曾经遇到过模组死机导致整条产线停摆,加了看门狗后再也没出过问题。

4.3.2 测试报告输出

测试报告我建议输出两种格式:

  • CSV格式:给MES系统导入用,每行一个模组,每列一个测试项
  • JSON格式:给研发分析用,包含详细的原始数据

CSV输出示例:

模组ID,测距精度@1m,测距精度@2m,帧率,温度漂移,总结果
TOF-001, PASS, PASS, PASS, FAIL, FAIL
TOF-002, PASS, PASS, PASS, PASS, PASS
TOF-003, PASS, FAIL, PASS, PASS, FAIL

嗯,说到这,测试软件框架的核心内容基本都覆盖了。上位机选C#还是Python,看你场景;固件烧录和校准,流程要标准化;自动化脚本,追求稳定和速度。这些都是在产线上真金白银换来的经验,希望能帮你少走弯路。


公众号:蓝海资料掘金营,微信deep3321