机器人基础之硬件篇
从这一篇文档开始,我们就开始正式步入机器人正题,我将介绍算法组相关的各类机器人基础知识。需要事先声明的是,这些文档只起到入门的作用,因为社区中很多详细的学习资料(这些大佬也相当厉害,我的工作也可以说只是整理他们的想法😇),所以相当一部分的内容我会点到为止,不会详细介绍底层,并且我会提供相关的书籍、视频或资料,因此期望大家看完文档后接着去学习相关的资料,得到更深的理解。
这篇文档是介绍与算法组相关的硬件知识,包括:相机、激光雷达、IMU、运算平台、低速IO外设。
相机
不同于大家最常接触到的单反、手机摄像头等相机,我们实际使用的相机是彩色工业相机。
工业相机可大致分为三类:
单目相机:顾名思义,即只有一个镜头的相机。单目相机是算法组最常用的相机,它结构简单,成本较低,适合安装到机器人上,但是却有一个缺陷:它无法获取像素的深度。深度即为像素到相机的距离,可以这样理解:我们肉眼来观测一个物体,是不是能够很容易感知到物体的远近,这种距离便是深度。相机中的每一帧图像便是照片,它以一种二维的形式来记录三维的物体,自然丢失了一个维度,丢失的便是深度。我们无法从单张图像中计算出深度,虽然我们可能通过某些特性(如近大远小)来得到图像中的大致距离(一些经典的视觉误判也会产生),但我们无法量化这一感觉。
或许你会想到,我们可以通过移动相机,通过多帧图像的对比来定量的计算出深度。显然这是可行的,但是即使我们计算出来这个值,这也是一个相对的值。试想看,我们将相机运动和场景大小同时放大两倍,是不是还是会得到同样的图像。同样,我们将这个大小乘上任意值,我们得到的图像都是一样的。这就说明一个问题,我们无法知道现实中场景的深度与我们单目计算出来的深度是否相差一个因子,即为尺度。我们无法通过单目图像来确定尺度,即为尺度不确定性。
根本原因是,单张图片无法计算出深度,这也导致双目相机和深度相机的出现。
我们车上的hik cs016-10uc 单目相机
双目相机:通常是左右两个镜头(也有上下的),与人眼一样,通过左右镜头图像的差异来计算出深度。我们叫左右两个镜头之间的距离为基线,双目相机能够探测到的深度距离与基线有关。双目相机现有的最大问题是视差的计算量很大,常需要GPU和FPGA来进行加速才能达到实时输出深度,故需要消耗很大算力资源。在RM中,因车上算力资源受限(也很少有队伍会在车上放一个雷霆大显卡😋),所以车上常用单目相机,雷达站则有队伍使用双目相机。
深度相机(RGB-D相机):可以通过红外结构光或Time-of-Flight(ToF)原理来主动像物体发射光并接收返回的光,从而测出深度。由于深度相机通过物理结构来计算深度,而不像双目相机通过软件计算,因此它不吃算力资源。但是由于极易受日光干扰,因此常用于室内,室外几乎不可用。
D435i深度相机
接下来介绍一下相机的几个重要参数(包括硬件参数和软件参数,这些也是相机选型的重要参考)
靶面尺寸:该参数即感光元件的面积大小,值越大表明面积越大,面积越大进光量就越大,信噪比(即真正的由光线引起的动作信号和噪声)自然会相应提高,对于暗光环境会有更好的成像效果,还有其他种种优势,这也是所谓的底大一级压死人。RM 主流 Type 1/1.8” 到 Type 1/3”
像元尺寸:顾名思义就是一个像素的实际面积有多大。在靶面尺寸相同的情况下,自然是拥有更大 像元尺寸的传感器的感光能力更强了(合并后的像素可以采集更多的光线)。主流在4μm 附近
最大帧率:相机每秒钟能够获取的图像数的最大值。一般来说,如果你的图像处理算法的速度够快,那么帧率越高越好,这能够保证你处理结果的实时性。一个相机能够支持的帧率是有限的,如hik cs016-10uc 最大帧率是249.1 fps @1440 × 1080 Bayer RG 8 (即在BayerRG8图像格式、1440x1080分辨率下的最大帧率为249.1fps)
曝光时间:每一帧图像的感光时间,其值愈大则画面的整体亮度越大,曝光时间过长过短都可能会出现 宽容度不够的情况(一片雪白或是漆黑无比),选择正确的曝光是算法能否奏效的关键。一般我们认为曝光时间超过 3000(μs)会导致画面模糊。(我们自家的exposure是用PID调的🥰)
增益:调节感光单元在进行电荷信号放大时的增益,对于图像的亮度和各颜色信息的保存都有影响。在低曝光的时候可以有效提高成像质量,但同时也可能提高噪声(不规则噪声信号也会被放大)。
链接方式:即相机连接到运算平台使用的硬件定义和通信协议。对于工业相机而言,我们会用到 的、最常见的有microUSB3和工业网口GigE,现代工业相机一般还支持自定义的可编程数字IO接口,即圆形6-pin数字IO。在RM中最常用的还是microUSB3,但因为容易松动,因此一定要选用有固定螺丝的接口。(不得不提在视觉群中每到比赛时都有队伍出现相机线松动导致自瞄掉的情况😅)
与相机直接相关的还有镜头,接下来介绍镜头的相关知识
焦距:一般使用的工业相机和USB相机配套的镜头是定焦但可以调节像距的。不同焦距的镜头其视距和视野范围不同,一般来说,视距大(看的远)的镜头,其视野范围小(可视角小);而视距短(看的近些)的镜头,视野范围大,典型的例子是广角镜头。我们常用的焦距有6mm、8mm和12mm,步兵用6mm和8mm,哨兵用8mm,飞镖用12mm。
光圈:光圈就是镜头前面可以开闭的小扇叶。不同光圈大小代表不同的镜头开度,其影响的是镜头的进光量。一般用f值刻画光圈的开合程度。(我个人会把光圈调的较小,来提高视觉灯光效果🥲)
由于凸透镜本身的性质和镜头制造的工艺问题,使得光线在通过镜头时无法保持物体在空间中原本 的位置关系而发生畸变,好在这些畸变都能够通过数学建模并由反向解算而得到还原。这需要我们 通过相机标定来去除这种畸变以便还原图像中物体的真实位置。畸变主要分为切向畸变和桶型畸 变,我们可以利用标定板和畸变的数学关系来进行相机标定,OpenCV中也有相关的函数可供调 用。这些具体细节我会在之后的篇章中详细介绍。
激光雷达
激光雷达的本质是“激光测距 + 扫描”,现在主流的测距方法有两种:
- 飞行时间法(ToF):向目标发射极短激光脉冲,精确测量脉冲从发出到返回的时间,除2乘光速便得出距离,通过记录发射时的方位角和俯仰角,得到每个点的三维坐标,无数个这样的点构成了点云。
- 调频连续波法(FMCW):发射线性调频的连续激光,回波与本地光发生干涉,通过频率差同时解算出距离和相对速度(利用多普勒效应),且抗干扰能力极强。
不同雷达的扫描方式也有不同
- 重复式扫描:扫描镜或收发模块以固定的周期和路径运动,激光束在空间划过的轨迹严格重复,因此每帧点云的扫描轨迹都是完全相同、精确重复的。导致存在固定盲区。
- 非重复式扫描:扫描轨迹永不重复,随时间推移像“画画”一样逐渐填满整个视场。两个带有楔形棱镜的光学元件以不同转速旋转,激光束穿过这两个棱镜后,出射方向会发生连续且不规则的变化。由于两个棱镜转速比不是整数,扫描轨迹形成一种类似花瓣、螺旋线的复杂图案,且永远不与之前的轨迹重合。
这是我现在手上的一个Livox MID-360激光雷达
下面是激光雷达的一些重要参数:
最大探测距离:雷达能探测到目标的最远距离。必须绑定目标反射率来看,同一雷达对高反射率目标和低反射率目标的探测距离差异巨大。
最小探测距离:雷达能正常聚焦成像的最近距离。这个参数决定了雷达的近场盲区的大小,过近会导致雷达看不见。
点云密度:每秒生成的激光点数量。点云密度直接决定了点云对场景细节的还原能力,一般我们会选择点云密度高的雷达。
视场角(FOV):雷达能覆盖的水平×垂直角度范围。这是雷达能够看清的视野范围。
在RM中,激光雷达一般起辅助作用来补全相机未能够获取到的信息,显然我们可以联合雷达和相机来计算出像素的深度。在哨兵上安装雷达可以进行SLAM(同时定位与建图)建立三维地图,在英雄上安装雷达可以用来辅助吊射,雷达站上使用雷达来获取高精度信息。
IMU
IMU,即惯性测量单元。一般包括加速度计和陀螺仪,气压计、磁力计等也可能被一起集成。
IMU是机器人身上相当重要的部件。对于一个六轴IMU,我们可以通过IMU测量出机器人当前姿态的角速度和角加速度,从而解算出机器人当前的姿态信息。
需要知道的是,IMU从芯片级就是一个有噪声、有零漂的传感器。虽然陀螺仪通过科氏力检测角速度已经最大程度降低了加速度对检测结果的影响,但难免还是会有检测误差。最常见的误差便是零漂,即IMU在禁止状态下仍然会产生一个很小的角速度输出。因为我们一般通过对陀螺仪的输出进行积分以获取位姿,因此零漂将持续引入误差,最终累计到不可接受的程度。消除噪声和零飘需要嵌入式侧的滤波,这些是电控同学的工作,我们需要做的便是信任下位机优化处理过的attitude数据。
IMU对于足式机器人的运动平衡极其重要,在RM中,实现小陀螺也需要IMU的数据,我们的自瞄系统也需要读取IMU提供的云台姿态信息。
运算平台
首先先申明一些概念,在机器人系统中,我们一般会有运算平台和MCU(单片机),运算平台处理一些上层任务(如自瞄),MCU处理下层任务(如电机驱动),因此便有上下位机之分,运算平台为上位机,MCU为下位机(当然社区中也有无下位机方案,详见RMCS:传送门 感兴趣的同学可以自行了解)
现在主流的运算平台分为两类:
- x86_64架构:典型代表为Intel NUC和各厂miniPC,x86_64架构一般与大家现在的电脑架构一致,也就说明你现在在你自己电脑上使用的一切软件在x86_64小电脑上都能够运行,这也是x86_64运算平台的显著优势:只是体积和功耗相对正常电脑较小,其他无区别。大部分软件开发者会提供x86_64包,大部分硬件驱动也支持x86_64。对比arm开发板,一般相对体积较大,功耗较高。对于硬件上,一般只有核显,且没有NPU,主要靠CPU来计算。
- arm64架构:典型代表为Nvidia Jetson nano、Nvidia Jetson NX、rk3588开发板以及各种派(如树莓派,但在RM中无法满足性能要求),arm开发板的优势主要在于有强大的GPU性能(如Nvidia Jetson NX)或强大的NPU性能(如rk3588开发板),对人工智能场景做过特化处理。相对x86小电脑,一般体积较小,功耗较低。但是相对x86大核来说CPU性能较弱,且最关键的一点是一些厂商对arm架构的支持不足,会出现软件兼容性问题和掉驱动问题(这也是现在社区中两派分歧的主要点🥵)
这是哨兵上的NUC
这是串腿上的NX
低速IO外设
这是上下位机通信的基础,主要为串口通信和CAN通信
串口通信:我们一般使用的是异步串行通信。串口形式上有RS-232接口、USB转串口、TTL电平串口,我们一般使用USB转串口在电脑上生成一个虚拟串口。串口通信是最常见、最基础的通信方式,它一般适用于一对一,需要通信双方提前约定好波特率、数据位、停止位、奇偶校验位。
串口通信协议是在通信发送开始时,先发送一位0表示开始发送,紧接着是8位的数据(相当于一次发 送一个byte,低位在前),然后是一位奇偶校验位(如果开启此功能),最后是一位停止位1(可 以设置不同的位数1.5、2)。以上便是串口通信的一个数据包。显然单单通过这样简单的协议(一 个数据包传送8位数据,最多可以表示256种状态),我们无法完成复杂的通信功能。因此,在此基 础上我们定制一套自己的通信协议以完成数据包更大、数据类型更复杂的通信,形成一个简单的协 议栈。
一个简单的协议需要包含帧头(标明数据包的开始)、数据内容(需要传送的数据)和帧尾 (表明一个数据包结束)。在数据包中可以增加用于数据校验的校验码,通信中常使用的有CRC(cyclic redundant check)、奇偶校验、和校验、哈希校验等。
struct SendRobotCmdData { static constexpr uint8_t ID = 0x01;
uint8_t cmd_ID = ID; uint32_t time_stamp;
uint8_t appear;
float pitch, yaw; float target_yaw, target_pitch;
float enable_yaw_diff, enable_pitch_diff; float v_yaw, v_pitch; float a_yaw, a_pitch;
uint8_t detect_color; auto to_tuple() const { return std::tie( cmd_ID, time_stamp, appear, pitch, yaw, target_yaw, target_pitch, enable_yaw_diff, enable_pitch_diff, v_yaw, v_pitch, a_yaw, a_pitch, detect_color ); }
} __attribute__((packed));这是自家自瞄系统串口通信协议的一部分,由于是上下位机之间通信,且用USB线连接,丢包极少可忽略,因此包内只写了帧头,下位机只要识别到了对应帧头就解析数据。
CAN通信:CAN其最大的特点就是稳定性(使用了差分通信的方法)和灵活性(设备只要挂载在总线上就可以使用,不需要额外的连接)。我们在比赛中大量使用了DJI生产的电机,这些电机都非常的智能,通过集成了mcu的电子调速器,我们可以跳过下位机,直接通过CAN与电调上的微控制器进行通信,将控制信息直接发送给电调从而控制电机的转动。当然也可以把信息通过can发送给电控再由下位机对执行单元进行控制。
CAN和串口一样,必然也需要一个通信协议。不同的是,其信号是通过其总线上电平的差值来表示的,这样能够有效抑制共模信号(因为噪声对两条线的影响通常是一样的,相减之后噪声的影响便被消除了),因此一般采用两条通信线。不过也最好连接地线,共地可以最大程度降低干扰。还有4-pin的CAN线,额外的一条线用于独立供电,适用于对信号质量特别高的场合,此电源专门为CAN收发器和信号电平供电。
使用CAN需要有些开发板上自带的40pin接口,这时只需要一个CAN收发器即可,或者我们使用USB转CAN转接器。
由于我们队的算法基本不使用CAN通信,因此不过多赘述了。
希望大家在看完此文档后接着去看《了解CV和RoboMaster视觉组》(也是CV圣经😍),里面有体系地介绍了算法组的工作,资料见:传送门 (群里也有)
如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时
