最近在调四旋翼用到的minimosd,虽然在网上可以找到一些资料,但是资料讲解的比较范范,有些步骤还是不知道应该怎么操作。经过这两天的摸索,终于搞清了OSD的开发过程,下面就做简要的述。
惭愧的很,其实研究飞行器pixhawk已经有近一年的时间了,也曾一直想写点什么,但总是被各种理由耽误和错过了。从这篇开始吧,养成一个写博客的好习惯,与人交流,也方便以后自己的再复习,以后再慢慢地把自己做四旋翼的过程也写下来。
pixahwk主控板上可以挂上一个摄像头,利用图传模块就可以远远的在显示屏看到摄像头中的景象,其实这些和飞控本身并没有关系。看过大疆的成像没有,上面还叠加了很多飞控本身的信息,如高度,速度等信息,这些信息叠加在视频信息上用的就是OSD模块,Osd的全称是on-screen display。OSD核心是利用字符发生芯片在显示器的屏幕上显示需要的字符,即在屏幕视频之上叠加数据,我们这里用的字符叠加芯片是MAX 7456。学会这些就可以定制一个自己的想要的信息。这里我其实是想在屏幕上显示飞机的“血条”。
下面让我们看看在视频之上叠加字符是个什么效果:
目前市面所有的飞控osd模块都是起源于google上的minimosd项目。该项目版本截止于12年的2.0。其硬件解决方案是采用atmega328芯片作为主控,配置相当于arduino nano,具有32kb的flash,2kb的sram以及1kb的eeprom;同时采用MAX7456作为字符叠加芯片,7456可以存储16*16=256个字符,针对pal或者ntsc信号完成字符叠加。
在软件方面,minimosd遵从mavlink协议,从串口获取封装为该协议的飞行数据,显示在视频之上,同时具有桌面配置程序,支持用户配置显示信息的内容与风格。
在minimosd2.0版本的基础之上,全球范围内出现了若干分支版本,其中比较著名的是托管在github上的minimosd-extra 2.4(https://github.com/diydrones/MinimOSD-Extra/wiki), 该版本在原版基础上扩展对mavlink传递信息的支持,可以显示更多的飞行数据,并且具备了简单的飞行统计功能,还改进了osd显示方案的切换方式。在此之外,也流传着一批针对国内用户的中文版osd固件,基本上都是基于minimosd-extra进行了简单改造。而杭州的playuav则大胆的对osd体系结构进行了改进,彻底改变了osd硬件架构,做出了一些很有新意的osd尝试(https://github.com/PlayUAV/),还有的就是杭州的playuav设置界面做的很是完善,对于只想简单定制飞行器信息的可以用这个,但是价格很昂贵,100多元,是minimosd的2-3倍价格。而minimosd的开发仍是以arduino为基础,其实也没必要看懂全部,知道从哪改就行了,所以改起来也不是很复杂。如果再了解一下mavlink就完美了。(估计后期我们可能还要最忌添加新的mavlink信息,后面再整理mavlink吧。)
下面我们先说一下开发环境大搭建,和部分工具的介绍吧。这也是有不少坑,最少先搭建环境,而后才能谈开发。先让大家看看我们用到的硬件吧。
这是整体的图,包括:1、首先的是摄像头和图传模块以及显示屏,这是一套的图传,用来显示摄像头的图像的。2、再者就是飞控和OSD模块,这一部分是用来在飞控中的mavlink信息解析出来之后叠加在视频之的。3、嗷,我们的目的是修改OSD定制我们想要的界面,那就还需要一个osd的程序下载模块,下面再贴上图。4、最后就是电源供电模块了,这里要两部分供电,pixhawk的5v供电,但是摄像头、图传、osd是12v供电的。下面贴的他们的图,自己随便看看好了。
说完了硬件部分,简单介绍一下软件吧。
1、pixhawk相关的软件就不说了。
2、固件下载工具可以用arduino本身的“上传”下载固件,也可以用ArduCAM OSD config这个软件进行下载固件。
3、MAX7456Charwizard.jar java的可执行工具,说到这还必须安装一个jdk,才可以,他是用来修改字库的。
4、ArduCAM OSD config这个软件还是很重要的,本身是配置显示界面的软件,和修改代码的效果是一致的,背后有代码在支持,两个可以配合着一起修改代码。同时刷字库也要用它,因为如果你想显示自己的一些汉字或自定义字符还是要改字符,改了之后再刷进osd。刚刚提过他也可以下载固件到osd。
5、上面是说用配置工具直接改osd,但是功能是受限的,配置中没有出现的还是要从角度中去改。改就要用到arduino开发工具。我们在这里就将近浪费了一天的时间,用过很多版本,最后只能使用arduino1.0.5的版本。下面我都会提供这些工具。
感觉说的有点乱了,说的简单点吧:arduino环境开发源码,配合配置工具一起修改,修改好后直接就可以下载到osd中使用了。但是要是想显示一些特别的字符汉字,就要自己改字库,再下载进去。
下面具体说说使用吧,不动手操作一遍,估计还是有迷糊的。
Minimosd源码分为三个部分,分别是osd功能固件ArduCam_Osd;配套库libraries以及osd的桌面配置工具OSD_Config。我们主要研读的代码是ArduCam_OSD,是烧录到osd片子上的主要功能代码。
ArduCam工程的主要功能文件包括两个头文件:OSD_Vars.h,OSD_Config.h以及三个源码定义文件ArduCAM_osd.ion,OSD_Panels.ino,MAVLink.ino。
OSD_Vars.h中定义的变量储存了从mavlink协议获取的飞行数据,这些飞行数据被解析后,保存在运行时内存sram中。
OSD_Config.h则负责与eeprom打交道,定义了配置信息在eeprom中的保存位置,便于minimosd启动后,从eeprom读取用户配置信息。
ArduCAM_osd.ino是minimosd的程序入口点,是整个程序的骨架。
OSD_Panels.ino是具体的绘制功能,每一帧视频,都有这些功能,将飞行器信息绘制到视频之上。
MavLink.ino则是用于对mavlink协议的解析,将收到的数据包按照mavlink协议解析,解析成功后将数据储存在ODS_Vars.h中定义的变量中。
整个minimosd的程序流程非常简单,如下图所示:
在minimosd通电启动后,首先调用到Setup()入口函数,实现如下流程。
1.初始化mavlink,设置了与飞控连接的串口波特率以及端口号
Serial.begin(TELEMETRY_SPEED);// setup mavlink port
mavlink_comm_0_port = &Serial;
然后向飞控发送数据请求,osd需要如下飞行数据
MAV_DATA_STREAM_EXTENDED_STATUS,
MAV_DATA_STREAM_RC_CHANNELS,
MAV_DATA_STREAM_POSITION,
MAV_DATA_STREAM_EXTRA1,
MAV_DATA_STREAM_EXTRA2
2.初始化7456,设置7456通信的spi端口,并且激活7456
pinMode(MAX7456_SELECT, OUTPUT); // OSD CS
digitalWrite(MAX7456_SELECT, HIGH); // unplug OSD
3.读取eeprom设置,OSD_Config.h中记录了不同的设置信息在eeprom中的存储位置,在这里逐次读取,如:
//****** First set of 8 Panels ******
uint16_t offset = OffsetBITpanel * panel;
setBit(panA_REG[panel], Cen_BIT, readEEPROM(panCenter_en_ADDR + offset));
panCenter_XY[0][panel]=readEEPROM(panCenter_x_ADDR + offset);
panCenter_XY[1][panel]=checkPAL(readEEPROM(panCenter_y_ADDR + offset));
这里要说一下minimosd中的panel结构,minimosd中设计了4组,每组8个,共32个数据槽位,每个槽位分别对应于需
要显示的一种飞行数据。针对每个槽位,可以开启或者关闭显示,亦可以设置该槽位在屏幕上的显示位置(x,y坐
标)。对于上述的32组槽位设置,minimosd支持两种方案,所以放到一个长度为2的数组中进行管理 。
下述代码是每个槽位是否显示的变量结构,每组8个槽位的1或者0正好对应于一个byte数据类型,因为有两套显示方
案,所以每组都是一个长度为2的byte数组
// Panel BIT registers
byte panA_REG[npanels] = {0b00000000};
byte panB_REG[npanels] = {0b00000000};
byte panC_REG[npanels] = {0b00000000};
byte panD_REG[npanels] = {0b00000000}
下述代码是槽位显示位置的变量结构,x和y坐标分别用byte来存储。同样,都可以应对两套显示方案。
// First 8 panels and their X,Y coordinate holders
byte panCenter_XY[2][npanels]; // = { 13,7,0 };
byte panPitch_XY[2][npanels]; // = { 11,1 };
byte panRoll_XY[2][npanels]; // = { 23,7 };
byte panBatt_A_XY[2][npanels]; // = { 23,1 };
byte panGPSats_XY[2][npanels]; // = { 2,12 };
byte panGPL_XY[2][npanels]; // = { 2,11 };
byte panGPS_XY[2][npanels]; // = { 2,13 };
byte panBatteryPercent_XY[2][npanels];
// ........
4.启动loop循环,无更多细节。
通电后,minimosd完成Setup中的所有功能,便进入了一个周而复始的循环工作过程,不断的调用Loop()函数,而每
个loop循环,则执行如下函数:
1.获取mavlink数据.根据mavlink的msgid,获知数据类型,后分别调用mavlink库中的函数对数据包进行解析,存储
到变量中。
if(mavlink_parse_char(MAVLINK_COMM_0, c, &msg, &status)) {
mavlink_active = 1;
//handle msg
switch(msg.msgid) {
case MAVLINK_MSG_ID_HEARTBEAT:
case MAVLINK_MSG_ID_SYS_STATUS:
osd_vbat_A = (mavlink_msg_sys_status_get_voltage_battery(&msg) / 1000.0f); //Battery voltage, in millivolts (1 = 1 millivolt)
osd_curr_A = mavlink_msg_sys_status_get_current_battery(&msg); //Battery current, in 10*milliamperes (1 = 10 milliampere)
osd_battery_remaining_A = mavlink_msg_sys_status_get_battery_remaining(&msg); //Remaining battery energy: (0%: 0, 100%: 100)
..
case MAVLINK_MSG_ID_GPS_RAW_INT:
2.字符绘制,这是osd的核心功能,针对允许显示的飞行数据,调用绘制该数据的函数
if(ISd(panel,Warn_BIT)) panWarn(panWarn_XY[0][panel], panWarn_XY[1][panel]); // this must be here so warnings are always checked
//Testing bits from 8 bit register A
if(ISa(panel,Cen_BIT)) panCenter(panCenter_XY[0][panel], panCenter_XY[1][panel]); //4x2
if(ISa(panel,Pit_BIT)) panPitch(panPitch_XY[0][panel], panPitch_XY[1][panel]); //5x1
if(ISa(panel,Rol_BIT)) panRoll(panRoll_XY[0][panel], panRoll_XY[1][panel]);
if(ISa(panel,BatA_BIT)) panBatt_A(panBatt_A_XY[0][panel], panBatt_A_XY[1][panel]); //7x1
//.........................
//.........................
我们以绘制电池电量panBatt_A为例,该函数代码如下:
void panBatt_A(int first_col, int first_line){
osd.setPanel(first_col, first_line);
osd.openPanel();
osd.printf("%c%5.2f%c", 0xE2, (double)osd_vbat_A, 0x8E);
osd.closePanel();
}
其中,osd.setPanel()函数是7456提供的开发接口,设置当前要绘制字符的位置。
Osd.printf()函数类似于标准c++的printf,是向7456当前的位置输出字符。在上述代码中,在eeprom读出的屏幕位置,绘制了从mavlink协议获取的osd_vbat_A变量的值。
可见其实画图也就是这4步而已:
osd.setPanel(first_col, first_line);//确定显示的位置
osd.openPanel(); //打开显示
osd.printf("%c%5.2f%c", 0xE2, (double)osd_vbat_A, 0x8E);//显示什么
osd.closePanel();//关闭
上面已经说明了画图就是这么简单,这里是通过源码修改的,建议 配合config工具 对于已经提供的界面只要设置就行了。两个修改都有效,能配置的就配置,自定义再修改源码。
如何自定义字符,自定义汉字呢,自定义图标。
有没有看到上面两幅字库的差距,在字符中有些已经被我改变了,将改变后的字库刷进OSD,索引相同的位置显示就不同了。
如何调用呢?感觉叫字符不合适,就叫它图标吧,包括汉字。
上面一共是16*16=256个字符,每个字符都要唯一的索引,即从0x00--0xFF刚好16*16=256个。比如在上面的图上,"自"的索引就是
0x808,这是全称,因为每个索引位置都不同,故用0x80就可以调用 “自”。“稳”的索引是0x81,“悬停”的索引是“\x82\x83”,“\x82\x83”这种事arduino源码中的写法。
这种图标是显示不能再用osd.printf函数,而是用osd.printf_P( PSTR())PSTR解析字符串就是对字库的索引,如刚开始我们这个OSD显示的logo是
osd.printf_P(PSTR("\x20\x20\x20\x20\xba\xbb\xbc\xbd\xbe|\x20\x20\x20\x20\xca\xcb\xcc\xcd\xce|
如何修改成自己想要的图标汉字,首先利用MAX7456 MCM Wizard工具(需要安装jdk),file--->open--->选择你要修改的字库
。选中某个位置后。利用后面的白框和黑框还有灰框就可以绘制自己的图标了,绘制好了,别忘了保存save character,
file下也有save as。画图标是时候可以按住鼠标左键,一直去画。
最后怎么更新字库呢?其实也就是OSD下载的问题了。需要下载模块arduino下载器,因为我们这个用的OSD其实还是一个arduino,从上面在arduino下开发应该也看的出来了。
minimosd刷机教程:
options下updata firmware刷写固件.hex updata charset 刷字库.mcm
⼀ 一般情况下先刷字库再刷固件,如果字库刷不了先刷固件,然后刷字库,然后再刷次固件!
补充:arduino下如何生成hex固件,让updata firmware刷写固(arduino可以直接下载的).
1): 在arduino工具的File->preferences中找到preferences.txt文件。
2):用记事本打开preferences.txt,选择hex文件存放的路径,在最后行加入 build.path=d:\arduino\MyHexDir,
3):关闭arduino。
4):关闭preferences.txt ,关闭时对话框显示是否保存,选择保存。
Note:1:hex文件存放的路径可以由自己来定。 2:以上操作时不连接arduino硬件。
3、配置如下图!
由于中文字库删除了一些显示项,请安如下图配置osd选项,配置好后save current下就写进了osd的eepom!
看这这一切写的有点复杂,其实并不是只要自己需要时自己动手操作一遍遍都清晰了。这里说了这么多只是想把有些
问题说的清楚些,让后人不必再浪费这个时间了。比如我当初就是考虑这些汉字怎么显示的也应该尝试了不下几十次,最终才想出“索引”这种方法。再比如我对这些索引的格式一点也不清楚,为什么和图片中不同,“自”明明是0x808但其实用0x80就行了,既然是0x80,其实源码中是写作\x80;再比如这些字库怎么修改呢,修改后再重新烧录进去,那不就可以索引到自己定义的图标了嘛。
就说这么多吧,按照我们项目的要求或许下面要研究一下mavlink,飞机那面添加新的信息,视频显示这边解析出来
,并自定义表达。就说这么多吧,都已经2016年8月7日18:19:42,该去吃饭了
(交流qq:1254281752,注明加好友原因)