马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
本帖最后由 Guc 于 2017-10-13 20:03 编辑
正贴开始:
最近在调四旋翼用到的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;
Serial.begin(TELEMETRY_SPEED);// setup mavlink portmavlink_comm_0_port = &Serial;然后向飞控发送数据请求,osd需要如下飞行数据 - MAV_DATA_STREAM_EXTENDED_STATUS,
- MAV_DATA_STREAM_RC_CHANNELS,
- MAV_DATA_STREAM_POSITION,
- MAV_DATA_STREAM_EXTRA1,
- </span>MAV_DATA_STREAM_EXTRA2
MAV_DATA_STREAM_EXTENDED_STATUS,MAV_DATA_STREAM_RC_CHANNELS,MAV_DATA_STREAM_POSITION,MAV_DATA_STREAM_EXTRA1, </span>MAV_DATA_STREAM_EXTRA2 2.初始化7456,设置7456通信的spi端口,并且激活7456 - pinMode(MAX7456_SELECT, OUTPUT); // OSD CS
- digitalWrite(MAX7456_SELECT, HIGH); // unplug OSD
pinMode(MAX7456_SELECT, OUTPUT); // OSD CSdigitalWrite(MAX7456_SELECT, HIGH); // unplug OSD3.读取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));
//****** 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}
// Panel BIT registersbyte 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];
- // ........
// First 8 panels and their X,Y coordinate holdersbyte 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:
- if(mavlink_parse_char(MAVLINK_COMM_0, c, &msg, &status)) {
mavlink_active = 1;//handle msgswitch(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
- //.........................
- //.........................
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]);//4x2if(ISa(panel,Pit_BIT)) panPitch(panPitch_XY[0][panel], panPitch_XY[1][panel]); //5x1if(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();
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();//关闭
osd.openPanel(); //打开显示 osd.printf("%c%5.2f%c", 0xE2, (double)osd_vbat_A, 0x8E);//显示什么 osd.closePanel();//关闭 上面已经说明了画图就是这么简单,这里是通过源码修改的,建议 配合config工具 对于已经提供的界面只要设置就行了。两个修改都有效,能配置的就配置,自定义再修改源码。
|