尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

TSL1401线性CCD智能车巡线:时序、曝光与自适应阈值实战

TSL1401线性CCD智能车巡线:时序、曝光与自适应阈值实战 简介这份PDF面向智能车竞赛光电组选手、嵌入式初学者及需要快速掌握线阵CCD的开发者系统讲解TSL1401线性CCD的工作原理与图像采集方法。内容从与面阵CCD的区别切入说明其只能采集一行128像素的一维图像并逐一解析AO、CLK、SI、VDD等引脚功能与内部移位寄存器、增益调整模块的作用。文档重点厘清积分时间即曝光时间的概念结合时序图指出前18个CLK为复位时间、第19个CLK到下一个SI为曝光时间并讨论最大100ms曝光限制及明暗环境下的曝光选择策略。编程部分介绍单次曝光法与连续曝光法两种常用方案对比二者在CPU占用和定时器使用上的差异并给出像素采集的时序要点与阈值选择思路。资源包为1个PDF文件大小342KB结构紧凑、图文并茂已有426人学习适合作为智能车光电组入门与调试的速查参考。1. 为什么 128 个像素的 TSL1401 至今还在智能车光电组里跑如果你拆过智能车光电组的板子大概率会在车头看到一个指甲盖大小的模块上面一颗 TSL1401 线性 CCD 加一个固定镜头输出一根模拟线直接进单片机的 ADC。它只有 128 个像素分辨率连早期功能机摄像头都不如却能在高速赛道上稳定巡线。原因很直接线性 CCD 只采集一行像素数据量小、时序确定、没有面阵 CCD 那种帧同步和坐标解析的负担控制环路的延迟可以压到很低。TSL1401 内部集成了 128 个光电二极管、移位寄存器和增益调整电路AO 引脚输出的模拟电压不需要外接运放就能直接进 ADC3V 到 5V 供电接线只有 SI、CLK、AO、VDD、GND 五根。它适合的场景也很明确需要一维亮度分布、采样周期固定、对实时性敏感的控制类应用比如巡线、边缘检测、简易光谱扫描。不适合拿它当摄像头用它天生只能看一行。2. TSL1401 的 SI/CLK 时序与像素采集原理2.1 引脚功能与内部信号链TSL1401 的引脚定义不复杂但每个信号在时序里的角色必须分清否则采集出来的 128 个值会整体错位。引脚名编号功能说明SI1串行输入决定数据输出序列的起始CLK2时钟输入控制电荷转移、像素输出和复位AO3模拟电压输出接单片机 ADCVDD4供电3V 至 5VGND6,7地NC5,8不连接内部结构上128 个像素各自带一个积分电路SI 通过开关逻辑控制每个像素的积分与复位CLK 通过移位寄存器把像素电压依次推到 AO。AO 前级有一个 Gain Trim 模块做增益调整所以官方文档明确说 AO 不需要再接运放。常见做法是 AO 直接进单片机 ADC 引脚中间只加一个 RC 低通滤掉时钟串扰。2.2 SI 与 CLK 的配合关系采集一帧的完整时序是这样的SI 拉高并保持至少 20ns然后开始给 CLK。前 18 个 CLK 是积分器复位时间从第 19 个 CLK 开始到下一个 SI 到来之间是曝光时间。每个 CLK 的下降沿AO 上会出现当前像素的模拟电压单片机在这个下降沿采样。128 个像素采完后还要再发第 129 个 CLK 结束本次采集否则移位寄存器状态不对下一帧会错位。// TSL1401 单帧采集时序伪代码基于 STM32 GPIO 翻转 void tsl1401_capture(uint16_t *pixels) { SI_HIGH(); delay_ns(30); // SI 高电平至少 20ns SI_LOW(); // 前 18 个 CLK复位积分器AO 数据丢弃 for (int i 0; i 18; i) { CLK_HIGH(); delay_ns(100); CLK_LOW(); delay_ns(100); } // 第 19 到第 146 个 CLK采集 128 个像素 for (int i 0; i 128; i) { CLK_HIGH(); delay_ns(100); CLK_LOW(); // 下降沿时 AO 有效 pixels[i] adc_read(); // 在下降沿后立即采样 delay_ns(100); } // 第 129 个 CLK结束本次采集 CLK_HIGH(); delay_ns(100); CLK_LOW(); }这段代码里delay_ns的实际值取决于单片机主频CLK 最高可以到 8MHz但大多数单片机的 ADC 转换时间跟不上所以实际 CLK 频率通常压到 1MHz 以下。adc_read()必须在 CLK 下降沿之后、下一个上升沿之前完成否则采到的是过渡值。如果发现 128 个像素整体偏移一位优先检查第 129 个 CLK 有没有发。2.3 曝光时间的物理含义与 100ms 上限TSL1401 文档里的“积分时间”就是曝光时间像素输出电压与光强和积分时间成正比。两个 SI 信号之间的间隔就是曝光时间前 18 个 CLK 的时间在 CLK 足够快时可以忽略。器件最大曝光时间是 100ms超过这个值图像不会更亮只会浪费采集周期。曝光时间的选择直接决定图像质量和控制周期。光线暗就加曝光光线强就减曝光这个逻辑和相机一样。但在智能车这种快速运动场景里加曝光有两个代价一是采集周期变长控制环路反应变慢二是运动模糊加重二值化后黑线边缘会糊成一片。我一般会把曝光时间控制在 2ms 到 10ms 之间具体值用示波器看 AO 波形保证最亮像素不饱和、最暗像素不贴地。3. 单次曝光与连续曝光两种编程方法怎么选3.1 单次曝光法的实现与 CPU 占用问题单次曝光法的思路是用一个 SI 周期专门曝光下一个 SI 周期采集。第一个 SI 周期内要发 129 个 CLK 把没用的 AO 输出丢掉第二个 SI 周期内采到的才是正确图像。两个 SI 之间用延时函数控制曝光时间。// 单次曝光法曝光和采集分离 void tsl1401_single_shot(uint16_t *pixels, uint32_t exposure_us) { // 第一个 SI 周期只曝光丢弃 AO SI_HIGH(); delay_ns(30); SI_LOW(); for (int i 0; i 129; i) { CLK_HIGH(); delay_ns(100); CLK_LOW(); delay_ns(100); } delay_us(exposure_us); // 曝光时间由软件延时控制 // 第二个 SI 周期正式采集 SI_HIGH(); delay_ns(30); SI_LOW(); for (int i 0; i 18; i) { CLK_HIGH(); delay_ns(100); CLK_LOW(); delay_ns(100); } for (int i 0; i 128; i) { CLK_HIGH(); delay_ns(100); CLK_LOW(); delay_ns(100); pixels[i] adc_read(); } CLK_HIGH(); delay_ns(100); CLK_LOW(); }exposure_us就是曝光时间单位微秒。这种写法的问题是delay_us期间 CPU 只能空转如果曝光 10ms这 10ms 里主控什么也干不了。采集周期不固定的场景可以用但智能车这种需要同时跑 PID 和舵机控制的系统单次法会明显拖累控制频率。3.2 连续曝光法与 PIT 定时器中断连续曝光法把曝光时间的控制交给单片机的周期定时器比如 PIT。PIT 中断周期就是曝光时间在中断里发 SI 和 CLK 完成采集。这样主循环可以继续跑控制算法CPU 利用率高很多。// 连续曝光法PIT 中断内完成一帧采集 volatile uint16_t ccd_buf[128]; volatile uint8_t ccd_ready 0; void PIT_IRQHandler(void) { PIT_CLEAR_FLAG(); SI_HIGH(); delay_ns(30); SI_LOW(); for (int i 0; i 18; i) { CLK_HIGH(); delay_ns(100); CLK_LOW(); delay_ns(100); } for (int i 0; i 128; i) { CLK_HIGH(); delay_ns(100); CLK_LOW(); delay_ns(100); ccd_buf[i] adc_read(); } CLK_HIGH(); delay_ns(100); CLK_LOW(); ccd_ready 1; // 通知主循环新数据就绪 }PIT 中断周期设成 5ms曝光时间就是 5ms 减去 129 个 CLK 的时间。CLK 周期 200ns 时129 个 CLK 约 26us相对 5ms 可以忽略。ccd_ready标志位让主循环知道什么时候可以读ccd_buf避免读到半帧数据。注意中断里的delay_ns会占用中断时间如果 CLK 频率太高导致中断执行时间接近 PIT 周期就要考虑用硬件 SPI 或定时器 PWM 来产生 CLK。3.3 两种方法的对比与选型建议对比项单次曝光法连续曝光法曝光时间控制软件延时PIT 中断周期CPU 占用高曝光期间空转低主循环不受影响采集周期不固定固定适用场景静态或慢速采集实时控制、智能车巡线实现难度低中需配置定时器中断选型逻辑很简单采集周期固定、系统对实时性有要求用连续曝光法只是偶尔读一次图像、不关心 CPU 占用单次曝光法更省事。智能车光电组基本都用连续曝光法PIT 周期就是控制周期的一部分。4. 基于亮度反馈的自适应曝光与阈值调节4.1 感光传感器接入与亮度采集拉普兰德线性 CCD 模块 V2 版本在 PCB 正面集成了一颗感光传感器供电和 TSL1401 一样是 3V 到 5V输出电流与环境亮度呈线性关系。把它的 Vout 接到单片机另一个 ADC 通道就能读到环境亮度值。// 读取环境亮度传感器 uint16_t read_ambient_light(void) { // 假设亮度传感器接在 ADC_Channel_1 adc_set_channel(ADC_CH1); adc_start_conversion(); while (!adc_conversion_done()); return adc_get_result(); // 返回 0~409512 位 ADC }这个值不需要很精确它的作用是给曝光时间和二值化阈值提供一个参考基准。我一般会在主循环里每 100ms 读一次亮度做滑动平均滤掉突变。4.2 自适应曝光时间的计算与上限保护根据亮度调曝光时间的基本关系是亮度低就加曝光亮度高就减曝光。但 TSL1401 最大曝光 100ms而且快速运动场景要避免长曝光所以必须设上限。// 根据环境亮度计算曝光时间PIT 周期单位 us uint32_t calc_exposure(uint16_t ambient) { // ambient 越大表示环境越亮 uint32_t exposure 8000 - (ambient * 6); // 线性映射示例参数 if (exposure 10000) exposure 10000; // 上限 10ms避免运动模糊 if (exposure 1000) exposure 1000; // 下限 1ms避免全黑 return exposure; }8000和6这两个参数需要根据实际模块和赛道光照标定。做法是在典型光照下采集图像看最亮像素是否接近 ADC 满量程、最暗像素是否还有区分度然后微调。上限设 10ms 而不是 100ms是因为智能车速度下 10ms 已经会产生明显运动模糊。如果环境实在太暗优先考虑补光而不是继续加曝光。4.3 动态阈值二值化Vth k*Vout b线性 CCD 巡线的核心是把 128 个灰度值二值化成黑白两态阈值选不好黑线要么断要么糊。固定阈值在环境光变化时必然失效所以用亮度传感器输出动态调阈值。// 动态阈值二值化 void binarize(uint16_t *pixels, uint16_t ambient, uint8_t *binary) { // Vth k * Vout bk 和 b 需标定 float k 0.5f; float b 200.0f; uint16_t vth (uint16_t)(k * ambient b); for (int i 0; i 128; i) { binary[i] (pixels[i] vth) ? 1 : 0; // 1 表示白0 表示黑 } }k和b的标定方法是在两种不同环境亮度下分别采集图像手动找到合适的阈值解二元一次方程。注意ambient和pixels必须用同一个 ADC 参考电压否则线性关系不成立。二值化后一般还要做边沿检测找到黑白跳变点计算赛道中心线。5. 镜头调焦、余弦效应与输出增益的工程处理5.1 调焦从 AO 波形判断清晰度线性 CCD 模块的镜头焦距固定调焦就是拧镜头在镜头座上的进出位置。判断调焦是否到位最直接的方法是看 AO 波形对着一幅有明暗边界的画面清晰时波形在边界处陡峭尖锐模糊时波形过渡平滑。我一般会写一个简单的串口输出把 128 个像素值打到上位机看曲线边拧边看直到边界跳变最陡。// 串口输出 128 个像素值用于调焦观察 void dump_pixels(uint16_t *pixels) { for (int i 0; i 128; i) { printf(%d,, pixels[i]); } printf(\r\n); }调焦时把模块对准几米外的一个黑白分界明显的目标观察波形中黑白跳变沿的斜率。斜率越大越清晰。注意调焦要在实际使用的曝光时间下进行曝光时间变了波形幅度会变但跳变沿的陡峭程度主要取决于焦距。5.2 余弦效应与边缘补偿线性 CCD 图像中间亮、两边暗这是透镜成像的余弦效应边缘光线斜入射到感光面中心光线垂直入射。这个现象在 128 个像素上表现为两端像素值系统性偏低。如果直接用固定阈值二值化边缘的黑线可能被误判成白。常见做法是做一个补偿系数表对两端像素乘以一个大于 1 的系数。系数可以通过对均匀白板采集图像取中间像素均值为基准算出每个像素的补偿系数。// 余弦效应补偿系数示例需实测标定 const float cos_comp[128] { 1.15, 1.14, 1.13, /* ... 中间省略 ... */ 1.13, 1.14, 1.15 }; void apply_cos_compensation(uint16_t *pixels) { for (int i 0; i 128; i) { pixels[i] (uint16_t)(pixels[i] * cos_comp[i]); } }补偿系数不要超过 1.3否则会把噪声也放大。如果边缘暗得厉害优先检查镜头和模块的安装角度机械上能解决就不要全靠算法补。5.3 AO 输出要不要加运放很多人纠结 TSL1401 的 AO 要不要加运放其实官方文档已经写明 AO 内置了增益调整器不需要外接运放。所谓加运放提高亮度本质和采集完电压后在单片机里做数字增益是一回事而且外接运放还会引入额外噪声和失调。我一般直接 AO 进 ADC如果觉得信号幅度不够在软件里乘一个增益系数就行。唯一需要加运放的情况是 AO 走线很长、阻抗匹配有问题那时候加的是缓冲器而不是放大器。本文还有配套的精品资源点击获取
返回列表