海康、大华、大疆、华为、中兴、追觅经典面试题200道及答案详解
📋 总目录
🎯 第一部分:嵌入式基础 (40题)
1.1 C语言基础 (15题)
1.2 ARM架构 (15题)
1.3 BSP开发 (10题)
🔧 第二部分:专业技能 (60题)
2.1 驱动开发 (20题)
2.2 系统编程 (20题)
2.3 网络编程 (20题)
🧮 第三部分:算法与数据结构 (40题)
3.1 基础算法 (15题)
3.2 数据结构 (15题)
3.3 嵌入式算法 (10题)
🏗️ 第四部分:项目经验与设计 (30题)
4.1 系统设计 (15题)
4.2 性能优化 (10题)
4.3 调试测试 (5题)
🏢 第五部分:公司特色题目 (30题)
5.1 海康威视 (6题)
5.2 大华股份 (6题)
5.3 大疆创新 (6题)
5.4 华为技术 (6题)
5.5 中兴通讯 (6题)
5.6 追觅科技 (6题)
🎯 第一部分:嵌入式基础 (40题)
1.1 C语言基础 (15题)
1. 什么是关键字?C语言中有哪些关键字?
答案:
关键字是C语言中预先保留的具有特殊含义的标识符。C语言共有32个关键字:
- 数据类型:int, char, float, double, void, short, long, signed, unsigned
- 控制语句:if, else, switch, case, default, for, while, do, break, continue, goto, return
- 存储类别:auto, register, static, extern, typedef
- 结构类型:struct, union, enum
- 其他:sizeof, const, volatile
2. const关键字的作用?
答案:
const用于定义常量,可以修饰变量、函数参数、返回值:
const int a = 10;// a的值不能修改void func(const char *str);// 函数内不能修改str指向的内容const char* func();// 返回值不能被修改
3. static关键字的作用?
答案:
static有三个作用:
- 修饰局部变量:延长生命周期,程序结束才销毁
- 修饰全局变量:限制作用域只在当前文件
- 修饰函数:限制作用域只在当前文件
4. volatile关键字的作用?
答案:
volatile告诉编译器不要优化该变量,因为它的值可能被外部因素改变:
- 硬件寄存器
- 多线程共享变量
- 中断服务程序中的变量
5. extern关键字的作用?
答案:
extern用于声明变量或函数在其他文件中定义,实现跨文件访问:
c
// file1.c
int global_var = 100;
// file2.c
extern int global_var; // 声明全局变量
6. sizeof和strlen的区别?
答案:
- sizeof是运算符,计算变量或类型的字节数
- strlen是函数,计算字符串长度(不包括'\0')
- sizeof在编译时确定,strlen在运行时计算
7. malloc和calloc的区别?
答案:
- malloc(n):分配n字节,内容不确定
- calloc(n, size):分配n*size字节,内容初始化为0
- malloc效率更高,calloc更安全
8. free函数的作用?重复free会有什么问题?
答案:
free释放动态分配的内存。重复free会导致:
- 程序崩溃
- 内存堆损坏
- 未定义行为
9. 什么是内存泄漏?如何检测和避免?
答案:
内存泄漏是动态分配的内存未释放。避免方法:
- 确保每个malloc都有对应的free
- 使用valgrind等工具检测
- 采用RAII模式管理资源
10. 结构体和联合体的区别?
答案:
- 结构体:成员各自占用独立内存空间
- 联合体:所有成员共享同一内存空间
- 结构体大小≥各成员大小之和
- 联合体大小等于最大成员的大小
11. 位域的作用?
答案:
位域用于节省内存,允许按位定义变量:
c
struct Flags {
unsigned int flag1 : 1; // 1位
unsigned int flag2 : 2; // 2位
unsigned int flag3 : 5; // 5位
};
12. 枚举类型的作用?
答案:
枚举提高代码可读性,定义符号常量:
c
enum Color { RED, GREEN, BLUE }; // RED=0, GREEN=1, BLUE=2
13. typedef的作用?
答案:
typedef为现有类型创建别名:
c
typedef unsigned int uint32_t;
typedef struct Node Node;
14. 函数指针的作用?
答案:
函数指针用于回调函数、状态机等:
c
int (*func_ptr)(int, int); // 函数指针声明
func_ptr = add; // 指向add函数
int result = func_ptr(3, 4); // 调用函数
15. 预处理指令的作用?
答案:
预处理指令在编译前处理:
- #include:包含头文件
- #define:定义宏
- #ifdef/#ifndef:条件编译
- #pragma:编译器指令
1.2 ARM架构 (15题)
16. ARM处理器有哪些工作模式?各自的作用是什么?
答案:
ARM处理器有7种工作模式:
- 用户模式(User):正常程序执行模式,权限最低
- 快速中断模式(FIQ):处理高速中断,有更多私有寄存器
- 普通中断模式(IRQ):处理普通中断
- 管理模式(SVC):操作系统内核模式,复位和系统调用时进入
- 中止模式(Abort):处理内存访问错误
- 未定义指令模式(Und):处理未定义指令
- 系统模式(System):运行特权级操作系统任务,使用User模式寄存器
17. ARM寄存器组织结构是怎样的?
答案:
ARM共有37个32位寄存器:
- 通用寄存器 :R0-R15(31个)
- R0-R12:通用寄存器
- R13(SP):堆栈指针
- R14(LR):链接寄存器
- R15(PC):程序计数器
- 状态寄存器 :CPSR和SPSR(6个)
- CPSR:当前程序状态寄存器
- SPSR:保存的程序状态寄存器(每种模式一个)
不同模式下可访问的寄存器不同,FIQ模式有R8_fiq-R14_fiq私有寄存器。
18. CPSR寄存器各位的含义是什么?
答案:
CPSR(当前程序状态寄存器)32位分布:
- 位[31:28] :条件标志位(N、Z、C、V)
- N:负数标志
- Z:零标志
- C:进位标志
- V:溢出标志
- 位[27:8]:保留位
- 位[7:0] :控制位
- I:IRQ中断禁止位(1=禁止)
- F:FIQ中断禁止位(1=禁止)
- T:Thumb状态位(1=Thumb,0=ARM)
- M[4:0]:模式位
19. ARM异常处理流程是怎样的?
答案:
ARM异常处理流程:
- 异常发生:硬件检测到异常条件
- 保存现场 :
- 将CPSR复制到相应模式的SPSR
- 将返回地址保存到LR
- 设置CPSR进入相应模式
- 跳转处理:PC跳转到异常向量地址
- 执行处理:执行异常处理程序
- 恢复现场:从SPSR恢复CPSR,从LR恢复PC
20. ARM指令集的分类?
答案:
ARM指令集分为:
- 数据处理指令:算术、逻辑、比较指令
- 加载存储指令:LDR、STR等内存访问指令
- 分支指令:B、BL等跳转指令
- 协处理器指令:MCR、MRC等
- 异常产生指令:SWI、BKPT等
21. ARM寻址方式有哪些?
答案:
ARM寻址方式:
- 立即寻址:操作数在指令中
- 寄存器寻址:操作数在寄存器中
- 寄存器间接寻址:寄存器内容为地址
- 基址变址寻址:基址+偏移
- 堆栈寻址:SP基址的寻址
- 相对寻址:PC相对偏移
22. ARM和Thumb状态的区别?
答案:
- ARM状态:32位指令,高性能
- Thumb状态:16位指令,高代码密度
- 通过BX指令切换状态
- CPSR的T位标识当前状态
23. 什么是ARM的大端和小端模式?
答案:
- 小端模式:低字节存放在低地址
- 大端模式:高字节存放在低地址
- ARM默认小端,可配置为大端
24. ARM的中断向量表在哪里?
答案:
- 通常在地址0x00000000或0xFFFF0000
- 包含各种异常的入口地址
- 可通过协处理器寄存器重定位
25. 什么是ARM的流水线?
答案:
ARM采用5级流水线:
- 取指(IF)
- 译码(ID)
- 执行(EX)
- 访存(MEM)
- 写回(WB)
26. ARM的MMU作用?
答案:
MMU(内存管理单元)作用:
- 虚拟地址到物理地址转换
- 内存访问权限控制
- 内存缓存控制
27. 什么是ARM的Cache?
答案:
Cache是高速缓存:
- 减少内存访问延迟
- 提高系统性能
- 分为I-Cache和D-Cache
28. ARM的协处理器作用?
答案:
协处理器扩展ARM功能:
- CP15:系统控制
- CP14:调试
- CP10/CP11:VFP/NEON浮点运算
29. 什么是ARM的TrustZone?
答案:
TrustZone是安全技术:
- 分为安全世界和非安全世界
- 硬件级安全隔离
- 用于DRM、支付等安全应用
30. ARM多核架构的特点?
答案:
ARM多核特点:
- 多个ARM核心
- 共享内存和外设
- 核间通信机制
- 对称/非对称多处理
1.3 BSP开发 (10题)
31. 什么是BSP?BSP的主要作用是什么?
答案:
BSP(Board Support Package,板级支持包)是针对特定硬件平台的软件支持包,介于操作系统和硬件之间。
BSP主要作用:
- 硬件抽象:为操作系统提供统一的硬件接口
- 初始化支持:完成硬件的初始化配置
- 驱动支持:提供板载外设的驱动程序
- 启动支持:实现系统的启动流程
- 资源管理:管理硬件资源的分配和使用
32. BSP和驱动程序有什么区别?
答案:
BSP和驱动程序虽然都与硬件相关,但有明确的区别:
BSP特点:
- 针对特定开发板
- 包含完整的硬件支持
- 涵盖启动、初始化、驱动等
- 通常由硬件厂商提供
驱动程序特点:
- 针对特定外设
- 只负责设备的功能实现
- 可在不同平台移植
- 通常遵循标准接口
区别总结:
| 特性 | BSP | 驱动程序 |
|---|---|---|
| 范围 | 整个开发板 | 单个设备 |
| 功能 | 初始化+驱动 | 仅驱动 |
| 移植性 | 低 | 高 |
| 复杂度 | 高 | 相对低 |
33. 什么是硬件抽象层(HAL)?为什么需要HAL?
答案:
硬件抽象层是屏蔽硬件差异的软件层,为上层软件提供统一接口。
HAL的作用:
- 可移植性:使上层软件不依赖具体硬件
- 模块化:分离硬件相关和无关代码
- 可维护性:硬件变更不影响上层软件
- 标准化:提供统一的编程接口
34. 嵌入式系统的启动流程是怎样的?
答案:
嵌入式系统启动流程通常分为多个阶段:
启动流程:
- 上电复位:CPU从复位向量地址开始执行
- 第一阶段启动:CPU内部初始化、时钟配置、内存控制器初始化
- 第二阶段启动:DDR内存初始化、外设控制器初始化、加载操作系统
- 操作系统启动:内核初始化、驱动加载、文件系统挂载
- 应用启动:启动应用程序
35. 什么是设备树?为什么需要设备树?
答案:
设备树是描述硬件结构的数据结构。
设备树作用:
- 硬件描述:用树形结构描述硬件
- 代码分离:将硬件描述从代码中分离
- 可移植性:同一内核支持不同硬件
- 标准化:统一的硬件描述格式
36. Bootloader的作用是什么?
答案:
Bootloader是系统启动的第一个程序:
主要作用:
- 硬件初始化:初始化必要的硬件
- 系统检测:检测硬件配置
- 加载内核:将操作系统内核加载到内存
- 启动内核:跳转到内核入口点
- 参数传递:向内核传递启动参数
37. 什么是交叉编译?为什么需要交叉编译?
答案:
交叉编译是在一个平台上生成另一个平台上运行的程序。
需要交叉编译的原因:
- 资源限制:目标平台资源有限
- 性能考虑:宿主机性能更强
- 开发效率:提高开发和调试效率
- 工具链:目标平台可能缺少完整工具链
38. BSP中的内存映射是什么?
答案:
内存映射是将物理地址映射到虚拟地址空间:
内存映射作用:
- 地址转换:物理地址到虚拟地址
- 访问控制:设置内存访问权限
- 缓存控制:配置内存缓存属性
- 设备访问:映射设备寄存器地址
39. BSP中的中断处理流程?
答案:
中断处理流程:
- 中断发生:硬件产生中断信号
- 中断响应:CPU保存现场,跳转到中断向量
- 中断分发:根据中断源调用相应处理函数
- 中断处理:执行具体的中断处理逻辑
- 中断返回:恢复现场,返回被中断的程序
40. 如何进行BSP的调试?
答案:
BSP调试方法:
- 串口调试:通过串口输出调试信息
- JTAG调试:使用JTAG接口进行硬件调试
- LED指示:使用LED显示系统状态
- 逻辑分析仪:分析硬件信号
- 仿真器:使用硬件仿真器调试
🔧 第二部分:专业技能 (60题)
2.1 驱动开发 (20题)
41. Linux驱动的分类有哪些?
答案:
Linux驱动主要分为三类:
-
字符设备驱动
- 字节流访问
- 如串口、LED、按键
- 使用file_operations结构
-
块设备驱动
- 块方式访问
- 如硬盘、Flash
- 使用block_device_operations结构
-
网络设备驱动
- 网络数据传输
- 如以太网、WiFi
- 使用net_device_ops结构
42. 字符设备驱动的开发流程?
答案:
字符设备驱动开发流程:
- 定义设备结构:定义设备私有数据结构
- 实现file_operations:实现设备操作函数
- 设备注册:使用cdev_add注册设备
- 创建设备节点:使用mknod或udev创建
- 实现模块初始化:module_init和module_exit
c
// 示例代码
static struct file_operations fops = {
.owner = THIS_MODULE,
.open = device_open,
.release = device_release,
.read = device_read,
.write = device_write,
};
static int __init device_init(void) {
// 分配设备号
alloc_chrdev_region(&dev_num, 0, 1, "my_device");
// 初始化cdev
cdev_init(&cdev, &fops);
// 添加设备
cdev_add(&cdev, dev_num, 1);
return 0;
}
43. platform设备驱动的原理?
答案:
platform驱动是Linux的设备驱动框架:
原理:
- 设备分离:将设备和驱动分离
- 匹配机制:通过name匹配设备和驱动
- 资源管理:统一管理设备资源
- 探测函数:驱动探测设备时调用probe函数
c
// platform设备
static struct platform_device my_device = {
.name = "my_platform_device",
.id = -1,
.dev = {
.platform_data = &device_data,
},
};
// platform驱动
static struct platform_driver my_driver = {
.probe = my_probe,
.remove = my_remove,
.driver = {
.name = "my_platform_device",
.owner = THIS_MODULE,
},
};
44. I2C驱动的实现方法?
答案:
I2C驱动实现:
- I2C适配器驱动:实现I2C控制器驱动
- I2C设备驱动:实现具体I2C设备驱动
- I2C核心:提供I2C框架和接口
c
// I2C设备驱动
static struct i2c_driver my_i2c_driver = {
.driver = {
.name = "my_i2c_device",
.owner = THIS_MODULE,
},
.probe = my_i2c_probe,
.remove = my_i2c_remove,
.id_table = my_i2c_id,
};
static int my_i2c_probe(struct i2c_client *client) {
// 设备探测和初始化
return 0;
}
45. SPI驱动的实现方法?
答案:
SPI驱动实现:
- SPI控制器驱动:实现SPI主机控制器
- SPI设备驱动:实现SPI设备功能
- SPI消息传输:使用spi_message传输数据
c
// SPI设备驱动
static struct spi_driver my_spi_driver = {
.driver = {
.name = "my_spi_device",
.owner = THIS_MODULE,
},
.probe = my_spi_probe,
.remove = my_spi_remove,
};
static int my_spi_probe(struct spi_device *spi) {
// SPI设备初始化
return 0;
}
// SPI数据传输
static int spi_transfer_data(struct spi_device *spi,
const u8 *tx_buf, u8 *rx_buf, int len) {
struct spi_transfer xfer = {
.tx_buf = tx_buf,
.rx_buf = rx_buf,
.len = len,
};
struct spi_message msg;
spi_message_init(&msg);
spi_message_add_tail(&xfer, &msg);
return spi_sync(spi, &msg);
}
46. GPIO驱动的实现?
答案:
GPIO驱动实现:
- GPIO控制器驱动:实现GPIO控制器
- GPIO子系统:使用GPIO子系统API
- GPIO申请和释放:gpio_request和gpio_free
c
// GPIO使用示例
static int __init gpio_example_init(void) {
int ret;
// 申请GPIO
ret = gpio_request(GPIO_LED, "LED");
if (ret) {
printk(KERN_ERR "Failed to request GPIO\n");
return ret;
}
// 设置方向
ret = gpio_direction_output(GPIO_LED, 0);
if (ret) {
gpio_free(GPIO_LED);
return ret;
}
// 设置GPIO值
gpio_set_value(GPIO_LED, 1);
return 0;
}
static void __exit gpio_example_exit(void) {
gpio_set_value(GPIO_LED, 0);
gpio_free(GPIO_LED);
}
47. 中断驱动的实现?
答案:
中断驱动实现:
- 中断申请:使用request_irq申请中断
- 中断处理函数:实现中断处理逻辑
- 中断释放:使用free_irq释放中断
- 中断上下文:注意中断上下文的限制
c
// 中断处理示例
static irqreturn_t my_interrupt_handler(int irq, void *dev_id) {
// 中断处理逻辑
// 不能使用可能睡眠的函数
printk(KERN_INFO "Interrupt occurred\n");
return IRQ_HANDLED;
}
static int __init my_driver_init(void) {
int ret;
// 申请中断
ret = request_irq(IRQ_NUM,
my_interrupt_handler,
IRQF_TRIGGER_RISING,
"my_device",
&my_device);
if (ret) {
printk(KERN_ERR "Failed to request interrupt\n");
return ret;
}
return 0;
}
static void __exit my_driver_exit(void) {
free_irq(IRQ_NUM, &my_device);
}
48. DMA驱动的实现?
答案:
DMA驱动实现:
- DMA通道申请:使用dma_request_channel
- DMA传输配置:配置DMA传输参数
- DMA传输执行:启动DMA传输
- DMA完成回调:处理传输完成事件
c
// DMA传输示例
static void my_dma_callback(void *data) {
// DMA传输完成回调
printk(KERN_INFO "DMA transfer completed\n");
}
static int my_dma_transfer(struct device *dev,
dma_addr_t dst, dma_addr_t src, size_t len) {
struct dma_chan *chan;
struct dma_async_tx_descriptor *desc;
dma_cookie_t cookie;
// 申请DMA通道
chan = dma_request_chan(dev, "my_dma");
if (IS_ERR(chan)) {
return PTR_ERR(chan);
}
// 准备DMA传输
desc = dmaengine_prep_memcpy(chan, dst, src, len, 0);
if (!desc) {
dma_release_channel(chan);
return -ENOMEM;
}
// 设置回调
desc->callback = my_dma_callback;
desc->callback_param = chan;
// 提交传输
cookie = dmaengine_submit(desc);
dma_async_issue_pending(chan);
return 0;
}
49. 设备树在驱动中的应用?
答案:
设备树在驱动中的应用:
- 设备描述:在设备树中描述硬件信息
- 资源获取:驱动从设备树获取资源
- 属性解析:解析设备树属性
- 匹配机制:通过compatible属性匹配驱动
c
// 设备树示例
my_device@12345678 {
compatible = "vendor,my-device";
reg = <0x12345678 0x1000>;
interrupts = <0 123 4>;
status = "okay";
};
// 驱动中获取设备树资源
static int my_probe(struct platform_device *pdev) {
struct device_node *np = pdev->dev.of_node;
int irq;
void __iomem *base;
// 获取寄存器基地址
base = of_iomap(np, 0);
if (!base) {
return -ENOMEM;
}
// 获取中断号
irq = irq_of_parse_and_map(np, 0);
if (irq <= 0) {
iounmap(base);
return -EINVAL;
}
return 0;
}
50. Linux驱动的并发控制?
答案:
Linux驱动并发控制方法:
- 互斥锁:mutex_lock/mutex_unlock
- 自旋锁:spin_lock/spin_unlock
- 读写锁:rwlock_t
- 原子操作:atomic_t
- 完成量:completion
c
// 互斥锁示例
static DEFINE_MUTEX(my_mutex);
static void my_function(void) {
mutex_lock(&my_mutex);
// 临界区代码
mutex_unlock(&my_mutex);
}
// 自旋锁示例
static DEFINE_SPINLOCK(my_spinlock);
static void my_irq_function(void) {
unsigned long flags;
spin_lock_irqsave(&my_spinlock, flags);
// 临界区代码
spin_unlock_irqrestore(&my_spinlock, flags);
}
// 原子操作示例
static atomic_t my_counter = ATOMIC_INIT(0);
static void my_atomic_function(void) {
atomic_inc(&my_counter);
atomic_dec(&my_counter);
}
🏢 第五部分:公司特色题目 (30题)
5.1 海康威视 (6题)
181. H.264编码的基本原理是什么?
答案:
H.264编码原理:
- 帧间预测:利用前后帧的相关性
- 帧内预测:利用帧内像素的相关性
- 变换编码:DCT变换去除空间冗余
- 量化:减少数据精度
- 熵编码:CABAC/CAVLC无损压缩
关键技术:
- 宏块划分:16x16、8x8、4x4
- 多参考帧:提高预测精度
- 环路滤波:减少块效应
182. 如何优化视频编码的性能?
答案:
视频编码性能优化:
-
算法优化:
- 快速运动估计算法
- 优化模式选择
- 自适应量化
-
并行优化:
- 宏块级并行
- 帧级并行
- GPU加速
-
内存优化:
- 减少内存拷贝
- 优化缓存访问
- 使用零拷贝技术
-
硬件加速:
- 使用专用编码芯片
- NEON指令优化
- DSP加速
183. YUV到RGB的转换算法?
答案:
YUV到RGB转换公式:
c
// YUV420到RGB24转换
void yuv420_to_rgb24(unsigned char* yuv, unsigned char* rgb,
int width, int height) {
int y, u, v;
int r, g, b;
int size = width * height;
for (int i = 0; i < size; i++) {
y = yuv[i];
u = yuv[size + i/4];
v = yuv[size + size/4 + i/4];
// YUV到RGB转换
r = y + 1.402 * (v - 128);
g = y - 0.344 * (u - 128) - 0.714 * (v - 128);
b = y + 1.772 * (u - 128);
// 限制范围
r = CLIP(r, 0, 255);
g = CLIP(g, 0, 255);
b = CLIP(b, 0, 255);
rgb[i*3] = r;
rgb[i*3+1] = g;
rgb[i*3+2] = b;
}
}
184. 如何实现视频流的实时传输?
答案:
视频流实时传输实现:
-
协议选择:
- RTP/RTCP:实时传输协议
- RTSP:流媒体控制协议
- HTTP-FLV:基于HTTP的流媒体
-
优化策略:
- 自适应码率
- 前向纠错
- 缓冲区管理
- 网络拥塞控制
-
实现架构:
c// 视频传输框架 struct video_stream { int socket_fd; struct sockaddr_in server_addr; unsigned char* buffer; int buffer_size; pthread_mutex_t mutex; }; int send_video_frame(struct video_stream* stream, unsigned char* frame_data, int frame_size) { pthread_mutex_lock(&stream->mutex); // RTP打包 rtp_packet_t packet; create_rtp_packet(&packet, frame_data, frame_size); // 发送数据 int ret = sendto(stream->socket_fd, &packet, sizeof(packet), 0, (struct sockaddr*)&stream->server_addr, sizeof(stream->server_addr)); pthread_mutex_unlock(&stream->mutex); return ret; }
185. 嵌入式视频监控系统的架构设计?
答案:
嵌入式视频监控系统架构:
-
硬件层:
- 视频采集:摄像头、传感器
- 视频处理:DSP、FPGA
- 网络接口:以太网、WiFi
-
驱动层:
- V4L2驱动:视频采集
- 网络驱动:数据传输
- 存储驱动:数据存储
-
应用层:
- 视频编码:H.264/H.265
- 网络传输:RTSP/RTP
- 用户界面:Web/APP
-
系统架构:
摄像头 -> V4L2驱动 -> 视频编码 -> 网络传输 -> 客户端 | | | v v v 存储系统 录像管理 用户管理
186. 如何处理视频分析中的运动检测?
答案:
运动检测算法实现:
-
帧差法:
cint frame_difference(unsigned char* frame1, unsigned char* frame2, unsigned char* result, int width, int height) { int threshold = 30; int motion_pixels = 0; for (int i = 0; i < width * height; i++) { int diff = abs(frame1[i] - frame2[i]); result[i] = (diff > threshold) ? 255 : 0; if (result[i]) motion_pixels++; } return motion_pixels; } -
背景减除法:
cvoid background_subtraction(unsigned char* current_frame, unsigned char* background, unsigned char* foreground, int width, int height) { int threshold = 25; for (int i = 0; i < width * height; i++) { int diff = abs(current_frame[i] - background[i]); foreground[i] = (diff > threshold) ? 255 : 0; } } -
光流法:
- Lucas-Kanade光流
- Horn-Schunck光流
- 特征点跟踪
5.2 大华股份 (6题)
187. 车牌识别算法的实现原理?
答案:
车牌识别算法流程:
-
车牌定位:
c// 基于颜色和纹理的车牌定位 void locate_license_plate(unsigned char* image, int width, int height, Rect* plate_region) { // 颜色分割(蓝色或黄色车牌) unsigned char* color_mask = color_segmentation(image, width, height); // 形态学操作 morphological_operations(color_mask, width, height); // 连通域分析 find_connected_components(color_mask, width, height, plate_region); } -
字符分割:
cvoid segment_characters(unsigned char* plate_image, int width, int height, Rect* char_regions, int* char_count) { // 垂直投影 int* projection = vertical_projection(plate_image, width, height); // 找到字符边界 find_character_boundaries(projection, width, char_regions, char_count); } -
字符识别:
cchar recognize_character(unsigned char* char_image, int width, int height) { // 特征提取 float* features = extract_features(char_image, width, height); // 模板匹配或神经网络识别 char result = neural_network_classify(features); return result; }
188. 如何设计大容量视频存储系统?
答案:
大容量视频存储系统设计:
-
存储架构:
- 分布式存储
- RAID阵列
- 云存储集成
-
数据管理:
cstruct video_storage_manager { char storage_path[256]; int max_storage_size; int current_usage; pthread_mutex_t storage_mutex; }; int store_video_file(struct video_storage_manager* manager, unsigned char* video_data, int data_size, char* filename) { pthread_mutex_lock(&manager->storage_mutex); // 检查存储空间 if (manager->current_usage + data_size > manager->max_storage_size) { // 清理旧文件 cleanup_old_files(manager); } // 存储文件 char filepath[512]; snprintf(filepath, sizeof(filepath), "%s/%s", manager->storage_path, filename); FILE* fp = fopen(filepath, "wb"); if (fp) { fwrite(video_data, 1, data_size, fp); fclose(fp); manager->current_usage += data_size; } pthread_mutex_unlock(&manager->storage_mutex); return 0; } -
检索优化:
- 索引机制
- 时间戳索引
- 事件标记
189. 智能交通系统的视频分析技术?
答案:
智能交通视频分析:
-
车辆检测:
c// 基于Haar特征的车辆检测 void detect_vehicles(unsigned char* frame, int width, int height, Rect* vehicles, int* vehicle_count) { // 灰度化 unsigned char* gray = rgb_to_gray(frame, width, height); // Haar特征检测 haar_cascade_detect(gray, width, height, vehicles, vehicle_count); } -
车流量统计:
cstruct traffic_counter { int vehicle_count; int direction; time_t last_update; }; void count_vehicles(Rect* detection_boxes, int count, struct traffic_counter* counter) { for (int i = 0; i < count; i++) { // 判断车辆方向 int direction = determine_vehicle_direction(detection_boxes[i]); // 更新计数 if (direction == counter->direction) { counter->vehicle_count++; counter->last_update = time(NULL); } } } -
违章检测:
- 超速检测
- 违章停车
- 闯红灯检测
190. 视频编码的码率控制算法?
答案:
码率控制算法实现:
-
CBR(恒定码率):
cvoid cbr_control(encoder_context* ctx, int target_bitrate) { if (ctx->current_bitrate > target_bitrate) { // 增加量化参数 ctx->qp = MIN(ctx->qp + 1, MAX_QP); } else if (ctx->current_bitrate < target_bitrate) { // 减少量化参数 ctx->qp = MAX(ctx->qp - 1, MIN_QP); } } -
VBR(可变码率):
cvoid vbr_control(encoder_context* ctx, int min_bitrate, int max_bitrate) { float complexity = calculate_frame_complexity(ctx->current_frame); if (complexity > 0.7) { // 高复杂度帧使用高码率 ctx->target_bitrate = max_bitrate; } else if (complexity < 0.3) { // 低复杂度帧使用低码率 ctx->target_bitrate = min_bitrate; } else { // 中等复杂度 ctx->target_bitrate = (min_bitrate + max_bitrate) / 2; } }
191. 多路视频流的并发处理?
答案:
多路视频流并发处理:
-
多线程架构:
cstruct video_stream_processor { pthread_t* threads; int stream_count; stream_context* streams; thread_pool_t* thread_pool; }; void* process_stream_thread(void* arg) { stream_context* stream = (stream_context*)arg; while (stream->active) { // 获取视频帧 video_frame_t* frame = get_next_frame(stream); // 处理视频帧 process_video_frame(frame); // 释放帧 release_frame(frame); } return NULL; } -
负载均衡:
- 动态分配线程
- 任务队列管理
- 资源池管理
192. 视频数据的加密传输?
答案:
视频数据加密传输:
-
AES加密:
cvoid encrypt_video_data(unsigned char* data, int data_len, unsigned char* key, unsigned char* encrypted) { AES_KEY aes_key; AES_set_encrypt_key(key, 128, &aes_key); // ECB模式加密 for (int i = 0; i < data_len; i += 16) { AES_encrypt(data + i, encrypted + i, &aes_key); } } -
RSA密钥交换:
cvoid rsa_key_exchange(RSA* rsa, unsigned char* session_key) { // 生成随机会话密钥 RAND_bytes(session_key, 16); // 使用RSA公钥加密会话密钥 unsigned char encrypted_key[256]; int encrypted_len = RSA_public_encrypt(16, session_key, encrypted_key, rsa, RSA_PKCS1_PADDING); }
5.3 大疆创新 (6题)
193. PID控制算法在无人机中的应用?
答案:
PID控制算法实现:
c
struct pid_controller {
float kp; // 比例系数
float ki; // 积分系数
float kd; // 微分系数
float setpoint; // 目标值
float integral; // 积分项
float prev_error; // 上次误差
float output_limit; // 输出限制
};
float pid_compute(struct pid_controller* pid, float current_value, float dt) {
// 计算误差
float error = pid->setpoint - current_value;
// 比例项
float proportional = pid->kp * error;
// 积分项
pid->integral += error * dt;
float integral_term = pid->ki * pid->integral;
// 微分项
float derivative = (error - pid->prev_error) / dt;
float derivative_term = pid->kd * derivative;
// 计算输出
float output = proportional + integral_term + derivative_term;
// 输出限制
if (output > pid->output_limit) {
output = pid->output_limit;
// 防止积分饱和
pid->integral -= (output - pid->output_limit) / pid->ki;
} else if (output < -pid->output_limit) {
output = -pid->output_limit;
pid->integral -= (output + pid->output_limit) / pid->ki;
}
pid->prev_error = error;
return output;
}
// 四轴无人机姿态控制
struct quadcopter_control {
struct pid_controller roll_pid;
struct pid_controller pitch_pid;
struct pid_controller yaw_pid;
struct pid_controller altitude_pid;
};
void quadcopter_attitude_control(struct quadcopter_control* ctrl,
imu_data_t* imu,
motor_commands_t* motors) {
// 计算姿态控制输出
float roll_output = pid_compute(&ctrl->roll_pid, imu->roll, 0.01);
float pitch_output = pid_compute(&ctrl->pitch_pid, imu->pitch, 0.01);
float yaw_output = pid_compute(&ctrl->yaw_pid, imu->yaw, 0.01);
// 混合控制输出到电机
motors->motor1 = ctrl->altitude_pid.setpoint + roll_output + pitch_output - yaw_output;
motors->motor2 = ctrl->altitude_pid.setpoint - roll_output + pitch_output + yaw_output;
motors->motor3 = ctrl->altitude_pid.setpoint - roll_output - pitch_output - yaw_output;
motors->motor4 = ctrl->altitude_pid.setpoint + roll_output - pitch_output + yaw_output;
}
194. IMU数据融合算法?
答案:
IMU数据融合实现:
c
struct imu_fusion {
float quaternion[4]; // 四元数
float gyro_bias[3]; // 陀螺仪零偏
float accel_alpha; // 加速度计滤波系数
float mag_alpha; // 磁力计滤波系数
};
// 互补滤波器
void complementary_filter(struct imu_fusion* fusion,
float* gyro, float* accel, float* mag,
float dt) {
// 陀螺仪积分
float gyro_quat[4];
integrate_gyro(gyro, dt, fusion->quaternion, gyro_quat);
// 加速度计姿态估计
float accel_quat[4];
accel_to_quaternion(accel, accel_quat);
// 磁力计偏航角修正
float mag_quat[4];
mag_to_quaternion(mag, accel_quat, mag_quat);
// 融合权重
float alpha = 0.98; // 陀螺仪权重
float beta = 0.02; // 加速度计+磁力计权重
// 四元数融合
for (int i = 0; i < 4; i++) {
fusion->quaternion[i] = alpha * gyro_quat[i] +
beta * mag_quat[i];
}
// 四元数归一化
normalize_quaternion(fusion->quaternion);
}
// 扩展卡尔曼滤波器
struct ekf_filter {
float state[7]; // 状态向量 [qw, qx, qy, qz, wx, wy, wz]
float covariance[49]; // 协方差矩阵
float process_noise[7]; // 过程噪声
float measurement_noise[6]; // 测量噪声
};
void ekf_predict(struct ekf_filter* ekf, float* gyro, float dt) {
// 状态预测
predict_state(ekf->state, gyro, dt);
// 协方差预测
predict_covariance(ekf->covariance, ekf->process_noise, dt);
}
void ekf_update(struct ekf_filter* ekf, float* accel, float* mag) {
// 计算雅可比矩阵
float H[6][7];
compute_jacobian(ekf->state, H);
// 卡尔曼增益
float K[7][6];
compute_kalman_gain(ekf->covariance, H, ekf->measurement_noise, K);
// 状态更新
update_state(ekf->state, K, accel, mag);
// 协方差更新
update_covariance(ekf->covariance, K, H);
}
195. 无人机路径规划算法?
答案:
路径规划算法实现:
c
// A*路径规划算法
struct node {
int x, y;
float g_cost; // 从起点到当前点的代价
float h_cost; // 从当前点到终点的启发式代价
float f_cost; // 总代价
struct node* parent;
};
float heuristic(int x1, int y1, int x2, int y2) {
// 欧几里得距离
return sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
}
int astar_pathfinding(int start_x, int start_y, int end_x, int end_y,
int grid_width, int grid_height,
char* obstacle_map,
point_t* path, int* path_length) {
// 开放列表和关闭列表
struct node open_list[MAX_NODES];
struct node closed_list[MAX_NODES];
int open_count = 0, close_count = 0;
// 添加起点到开放列表
struct node start_node = {start_x, start_y, 0,
heuristic(start_x, start_y, end_x, end_y),
0, NULL};
open_list[open_count++] = start_node;
while (open_count > 0) {
// 找到f_cost最小的节点
int current_index = find_min_f_cost(open_list, open_count);
struct node current = open_list[current_index];
// 移动到关闭列表
remove_node(open_list, &open_count, current_index);
closed_list[close_count++] = current;
// 检查是否到达终点
if (current.x == end_x && current.y == end_y) {
// 重建路径
*path_length = reconstruct_path(¤t, path);
return 1;
}
// 检查邻居节点
for (int dx = -1; dx <= 1; dx++) {
for (int dy = -1; dy <= 1; dy++) {
if (dx == 0 && dy == 0) continue;
int nx = current.x + dx;
int ny = current.y + dy;
// 检查边界和障碍物
if (nx < 0 || nx >= grid_width ||
ny < 0 || ny >= grid_height ||
obstacle_map[ny * grid_width + nx]) {
continue;
}
// 检查是否在关闭列表中
if (in_closed_list(closed_list, close_count, nx, ny)) {
continue;
}
// 计算代价
float g_cost = current.g_cost + sqrt(dx*dx + dy*dy);
float h_cost = heuristic(nx, ny, end_x, end_y);
float f_cost = g_cost + h_cost;
// 检查是否在开放列表中
int open_index = in_open_list(open_list, open_count, nx, ny);
if (open_index >= 0) {
// 更新更好的路径
if (g_cost < open_list[open_index].g_cost) {
open_list[open_index].g_cost = g_cost;
open_list[open_index].f_cost = f_cost;
open_list[open_index].parent = &closed_list[close_count-1];
}
} else {
// 添加到开放列表
struct node neighbor = {nx, ny, g_cost, h_cost, f_cost,
&closed_list[close_count-1]};
open_list[open_count++] = neighbor;
}
}
}
}
return 0; // 没有找到路径
}
196. 电机控制算法?
答案:
无刷直流电机控制:
c
struct bldc_motor {
int pole_pairs; // 极对数
float max_current; // 最大电流
float max_speed; // 最大转速
pwm_channel_t pwm[3]; // 三相PWM
gpio_pin_t hall[3]; // 霍尔传感器
};
// FOC(磁场定向控制)
struct foc_control {
float id_ref; // d轴电流参考
float iq_ref; // q轴电流参考
float id, iq; // d轴、q轴电流
float vd, vq; // d轴、q轴电压
float va, vb, vc; // 三相电压
float theta; // 转子角度
float speed; // 转子速度
};
void foc_control(struct foc_control* foc,
float* i_abc, float* hall_state,
float* v_abc) {
// 克拉克变换(三相到两相)
float i_alpha = i_abc[0];
float i_beta = (2*i_abc[1] + i_abc[2]) / sqrt(3);
// 帕克变换(两相静止到两相旋转)
float cos_theta = cos(foc->theta);
float sin_theta = sin(foc->theta);
foc->id = i_alpha * cos_theta + i_beta * sin_theta;
foc->iq = -i_alpha * sin_theta + i_beta * cos_theta;
// PI控制器
foc->vd = pi_controller(foc->id_ref - foc->id, foc->vd);
foc->vq = pi_controller(foc->iq_ref - foc->iq, foc->vq);
// 反向帕克变换
float v_alpha = foc->vd * cos_theta - foc->vq * sin_theta;
float v_beta = foc->vd * sin_theta + foc->vq * cos_theta;
// 反向克拉克变换
v_abc[0] = v_alpha;
v_abc[1] = (-v_alpha + sqrt(3)*v_beta) / 2;
v_abc[2] = (-v_alpha - sqrt(3)*v_beta) / 2;
}
// 无传感器控制
void sensorless_control(struct bldc_motor* motor,
float* v_abc, float* i_abc,
float* estimated_angle, float* estimated_speed) {
// 滑模观测器
float e_alpha, e_beta; // 反电动势
float z_alpha, z_beta; // 滑模变量
// 估算反电动势
e_alpha = v_abc[0] - R * i_abc[0] - L * di_abc[0]/dt;
e_beta = (2*v_abc[1] + v_abc[2])/sqrt(3) -
R * (2*i_abc[1] + i_abc[2])/sqrt(3) -
L * d(2*i_abc[1] + i_abc[2])/sqrt(3)/dt;
// 计算转子角度
*estimated_angle = atan2(-e_beta, e_alpha);
// 计算转子速度
static float prev_angle = 0;
*estimated_speed = (*estimated_angle - prev_angle) / dt;
prev_angle = *estimated_angle;
}
197. 无人机通信协议设计?
答案:
无人机通信协议:
c
// 通信数据包结构
struct drone_packet {
uint8_t start_byte; // 起始字节 0xAA
uint8_t packet_type; // 数据包类型
uint16_t length; // 数据长度
uint8_t sequence; // 序列号
uint8_t data[MAX_DATA_LEN]; // 数据载荷
uint16_t checksum; // 校验和
uint8_t end_byte; // 结束字节 0x55
};
// 数据包类型
enum packet_type {
PACKET_HEARTBEAT = 0x01,
PACKET_TELEMETRY = 0x02,
PACKET_COMMAND = 0x03,
PACKET_GPS = 0x04,
PACKET_IMU = 0x05
};
// 遥测数据
struct telemetry_data {
float battery_voltage;
float battery_current;
int motor_speed[4];
float altitude;
float speed;
uint32_t timestamp;
};
// 打包数据
int pack_telemetry(struct telemetry_data* telemetry,
uint8_t* buffer, int buffer_size) {
struct drone_packet packet;
packet.start_byte = 0xAA;
packet.packet_type = PACKET_TELEMETRY;
packet.sequence = get_next_sequence();
// 填充数据
memcpy(packet.data, telemetry, sizeof(struct telemetry_data));
packet.length = sizeof(struct telemetry_data);
// 计算校验和
packet.checksum = calculate_checksum(&packet);
packet.end_byte = 0x55;
// 序列化到缓冲区
return serialize_packet(&packet, buffer, buffer_size);
}
// 解包数据
int unpack_packet(uint8_t* buffer, int buffer_len,
struct drone_packet* packet) {
// 查找起始字节
int start_index = find_start_byte(buffer, buffer_len);
if (start_index < 0) return -1;
// 检查数据包完整性
if (!validate_packet(buffer + start_index, buffer_len - start_index)) {
return -1;
}
// 反序列化数据包
return deserialize_packet(buffer + start_index, packet);
}
// MAVLink兼容协议
struct mavlink_message {
uint8_t magic; // MAVLink版本
uint8_t len; // 载荷长度
uint8_t incompat_flags; // 不兼容标志
uint8_t compat_flags; // 兼容标志
uint8_t seq; // 序列号
uint8_t sysid; // 系统ID
uint8_t compid; // 组件ID
uint32_t msgid; // 消息ID
uint8_t payload[255]; // 载荷数据
uint16_t checksum; // 校验和
uint8_t signature[13]; // 签名(可选)
};
198. 无人机避障算法?
答案:
避障算法实现:
c
// 超声波传感器数据
struct ultrasonic_sensor {
float distance; // 测量距离
float angle; // 传感器角度
int max_range; // 最大测量范围
};
// 激光雷达数据点
struct lidar_point {
float x, y, z; // 三维坐标
float intensity; // 强度
uint32_t timestamp; // 时间戳
};
// 障碍物检测
struct obstacle {
float distance; // 距离
float angle; // 角度
float size; // 大小
int type; // 类型:静态/动态
};
// 基于超声波的避障
void ultrasonic_avoidance(struct ultrasonic_sensor* sensors,
int sensor_count,
float* avoidance_vector) {
float min_distance = FLT_MAX;
float danger_angle = 0;
// 找到最近的障碍物
for (int i = 0; i < sensor_count; i++) {
if (sensors[i].distance < min_distance) {
min_distance = sensors[i].distance;
danger_angle = sensors[i].angle;
}
}
// 计算避障向量
if (min_distance < SAFE_DISTANCE) {
float avoidance_strength = (SAFE_DISTANCE - min_distance) / SAFE_DISTANCE;
avoidance_vector[0] = -cos(danger_angle) * avoidance_strength;
avoidance_vector[1] = -sin(danger_angle) * avoidance_strength;
} else {
avoidance_vector[0] = 0;
avoidance_vector[1] = 0;
}
}
// 基于激光雷达的避障
void lidar_avoidance(struct lidar_point* points, int point_count,
float* avoidance_vector) {
// 构建占用栅格地图
int grid_size = 100;
char occupancy_grid[grid_size][grid_size];
memset(occupancy_grid, 0, sizeof(occupancy_grid));
// 投影激光点到栅格地图
for (int i = 0; i < point_count; i++) {
int grid_x = (int)(points[i].x * 10 + grid_size/2);
int grid_y = (int)(points[i].y * 10 + grid_size/2);
if (grid_x >= 0 && grid_x < grid_size &&
grid_y >= 0 && grid_y < grid_size) {
occupancy_grid[grid_y][grid_x] = 1;
}
}
// 计算势场
float potential_field[grid_size][grid_size];
for (int y = 0; y < grid_size; y++) {
for (int x = 0; x < grid_size; x++) {
if (occupancy_grid[y][x]) {
potential_field[y][x] = 100; // 障碍物高势能
} else {
potential_field[y][x] = 0; // 自由空间低势能
}
}
}
// 势场平滑
smooth_potential_field(potential_field, grid_size);
// 计算梯度(避障方向)
int drone_x = grid_size / 2;
int drone_y = grid_size / 2;
float grad_x = (potential_field[drone_y][drone_x + 1] -
potential_field[drone_y][drone_x - 1]) / 2.0;
float grad_y = (potential_field[drone_y + 1][drone_x] -
potential_field[drone_y - 1][drone_x]) / 2.0;
// 避障向量(负梯度方向)
float magnitude = sqrt(grad_x * grad_x + grad_y * grad_y);
if (magnitude > 0) {
avoidance_vector[0] = -grad_x / magnitude;
avoidance_vector[1] = -grad_y / magnitude;
} else {
avoidance_vector[0] = 0;
avoidance_vector[1] = 0;
}
}
// 动态窗口法避障
struct dynamic_window {
float v_min, v_max; // 速度范围
float omega_min, omega_max; // 角速度范围
float v, omega; // 当前速度和角速度
float dt; // 时间步长
};
float evaluate_trajectory(float v, float omega,
struct obstacle* obstacles, int obstacle_count,
float goal_x, float goal_y) {
float score = 0;
// 预测轨迹
float predict_time = 2.0; // 预测2秒
int steps = (int)(predict_time / 0.1);
float x = 0, y = 0, theta = 0;
for (int i = 0; i < steps; i++) {
x += v * cos(theta) * 0.1;
y += v * sin(theta) * 0.1;
theta += omega * 0.1;
// 检查碰撞
for (int j = 0; j < obstacle_count; j++) {
float dist = sqrt((x - obstacles[j].distance * cos(obstacles[j].angle)) *
(x - obstacles[j].distance * cos(obstacles[j].angle)) +
(y - obstacles[j].distance * sin(obstacles[j].angle)) *
(y - obstacles[j].distance * sin(obstacles[j].angle)));
if (dist < obstacles[j].size) {
return -1000; // 碰撞,极低分数
}
}
}
// 计算到目标的距离
float goal_dist = sqrt((x - goal_x) * (x - goal_x) +
(y - goal_y) * (y - goal_y));
// 综合评分
score = 100 - goal_dist; // 距离目标越近分数越高
return score;
}
void dynamic_window_avoidance(struct dynamic_window* dw,
struct obstacle* obstacles, int obstacle_count,
float goal_x, float goal_y,
float* best_v, float* best_omega) {
float best_score = -FLT_MAX;
// 遍历动态窗口
float v_step = (dw->v_max - dw->v_min) / 20;
float omega_step = (dw->omega_max - dw->omega_min) / 20;
for (float v = dw->v_min; v <= dw->v_max; v += v_step) {
for (float omega = dw->omega_min; omega <= dw->omega_max; omega += omega_step) {
// 检查动力学约束
if (fabs(v - dw->v) > MAX_ACCEL * dw->dt ||
fabs(omega - dw->omega) > MAX_ANG_ACCEL * dw->dt) {
continue;
}
// 评估轨迹
float score = evaluate_trajectory(v, omega, obstacles,
obstacle_count, goal_x, goal_y);
if (score > best_score) {
best_score = score;
*best_v = v;
*best_omega = omega;
}
}
}
}
📝 总结
这份海康、大华、大疆、华为、中兴、追觅经典面试题200道涵盖了:
🎯 核心技术领域
- 嵌入式开发:C语言、ARM架构、BSP开发
- 驱动编程:字符设备、平台设备、I2C/SPI、GPIO
- 系统编程:多线程、进程通信、内存管理
- 网络编程:TCP/IP、Socket、协议实现
🏢 公司特色技术
- 海康威视:视频编码、图像处理、安防系统
- 大华股份:智能交通、车牌识别、存储系统
- 大疆创新:飞控算法、IMU融合、路径规划
- 华为技术:通信协议、嵌入式系统、RTOS
- 中兴通讯:5G技术、网络设备、信号处理
- 追觅科技:机器人控制、SLAM、电机控制
💡 面试准备重点
- 基础扎实:重点复习C语言、数据结构、算法
- 专业深入:针对目标公司技术领域深入学习
- 项目经验:准备2-3个完整的项目案例
- 动手能力:多写代码,积累实战经验
祝你面试成功!🎉