编译pixhawk环境搭建
参考原网页进行说明,有两种搭建环境的方式。
第一种为自动执行脚本(推荐),较为方便,但有问题需解决。
由于使用apt-get update时部分文件无法下载,导致脚本执行不完整,故可将ardupilot/Tools/scripts/install-prereqs-ubuntu.sh文件里的$APT_GET update行用#注释掉。
然后操作如下:
1 | ~ $ sudo apt-get -qq -y install git |
第二种为手动搭建环境,这里只提出一些主要的注意事项。
需要特定的交叉编译器,更多参考搭建px4原生开发环境的文章。
1
2
3
4
5
6
7
8
9
10
11
12pushd .
# => 卸载新版的gcc-arm-none-eabi
~ $ sudo apt-get remove gcc-arm-none-eabi
~ $ wget https://launchpadlibrarian.net/186124160/gcc-arm-none-eabi-4_8-2014q3-20140805-linux.tar.bz2
# => 安装下载好的gcc-arm-none-eabi
~ $ tar xjvf gcc-arm-none-eabi-4_8-2014q3-20140805-linux.tar.bz2
~ $ sudo mv gcc-arm-none-eabi-4_8-2014q3 /opt
~ $ exportline="export PATH=/opt/gcc-arm-none-eabi-4_8-2014q3/bin:\$PATH"
~ $ if grep -Fxq "$exportline" ~/.profile; then echo nothing to do ; else echo $exportline >> ~/.profile; fi
# => 使路径生效
~ $ . ~/.profile
popd安装ccache加快编译速度。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18~ $ sudo apt-get install ccache
~ $ cd /usr/lib/ccache
~ $ sudo ln -s /usr/bin/ccache arm-none-eabi-g++
~ $ sudo ln -s /usr/bin/ccache arm-none-eabi-gcc
```
然后将`export PATH=/usr/lib/ccache:$PATH`加入到~/.profile中。
- 安装make, gawk,genromfs等linux开发工具。
- 权限:`sudo usermod -a -G dialout $USER`。
> `Tip`:编译过程中主要问题:
>1、考虑更新子模块,所有的子模块都放在modules/目录
>
> ```sh
> git submodule init
> git submodule update --recursive
> #或者一行就行
> git submodule update --init --recursive
>
2、如果以前编译正确,更新子模块后出现问题,
make px4-clean
后重新编译3、如果子模块更新的时候出现如下错误:
1
2
3 > fatal: 目标路径 'src/lib/ecl' 已经存在,并且不是一个空目录。
> 无法克隆 'https://github.com/PX4/ecl.git' 到子模组路径 'src/lib/ecl'
>
直接删除src/lib/ecl即可。
参与贡献
wiki知识点:
- 创建分支并改变一些代码:fork源仓库,克隆到本地,更改后推送到fork仓库。
- 保持代码更新:添加upstream远程官方库;更新
git fetch upstream
,fetch与pull的区别;重置当前的分支git rebase upstream/master
,这里可能有冲突需要解决;更新子模块;推送的fork库git push origin master
- 提交分支到master:确保每次提交只是做了一件事情;简洁易懂的注释;清理本地提交历史;推送到本地分支
git push -f origin master
;创建上拉请求;在Pull Request
页面选择New pull request
按钮;选择需要提交的分支然后点击Click to create pull request for this comparison
(base branch 是远程官方分支, head branch 是自己要提交的分支,这样做可以在任意时间段进行提交);每个参与者都会收到新请求消息;管理pull requests
;查看proposed changes
;Pull request
谈论;一段时间后可以查看long-running pull requests
下面我以ardupilot这个开源项目为例子做一个实际的演示:
下载源码并设置源,这一步只需进行一次就可以了。
1
2
3
4
5
6
7
8
9
10
11
12#首先进入https://github.com/ArduPilot/ardupilot进行fork
#然后如下下载源码
~ $ git clone git@github.com:your_github_name/ardupilot.git
#设置官方更新源,一般origin 默认已经设置
~ $ git remote add upstream git@github.com:ArduPilot/ardupilot.git
#查看设置源效果
~ $ git remote -v
#正常显示如下
origin git@github.com:your_github_name/ardupilot.git (fetch)
origin git@github.com:your_github_name/ardupilot.git (push)
upstream https://github.com/diydrones/ardupilot.git (fetch)
upstream https://github.com/diydrones/ardupilot.git (push)更改代码并推送到fork库。
1
2
3
4
5
6
7
8#首先添加更改的文件, .代表添加所有更改的文件
~ $ git add .
#查看添加状况
~ $ git status
#添加更改注释
~ $ git commit -m "your comment"
#推送到fork库
~ $ git push origin master保持与官方源代码同步,这一步一般与步骤2结合,保证推送到fork库时是在最新的代码上进行的更改。
1
2
3
4
5
6
7
8~ $ git fetch upstream master
#重置当前的分支
~ $ git rebase upstream/master
#一般这里都需要更新子模块
~ $ git submodule update --init --recursive
#查看更新结果
~ $ git status
#如果更新后产生了冲突,先解决冲突再按照步骤2进行add/commit在
Pull Request
页面选择New pull request
按钮;选择需要提交的分支然后点击Click to create pull request for this comparison
(base branch 是远程官方分支, head branch 是自己要提交的分支,这样做可以在任意时间段进行提交)
代码库
原文参考这里!!!
1、ArduPilot的基本框架分为5个主要部分:
- vehicle目录
- AP_HAL (Hardware Abstraction Layer):能使ArduPilot移植到不同的平台;目录为 libraries/AP_HAL;
- libraries
- tools目录
- 外部支持代码
2、makefiles文件是在mk/directory
目录里,为每个类型的支持定义编译规则,这里有一些辅助的make目标:
- make clean – clean the build for non-px4 targets
- make px4-clean – completely clean the build for PX4 targets
- make px4-cleandep – cleanup just dependencies for PX4 targets
3、探索自己的代码的第一步是使用库的example sketches。知道library API和约定在ArduPilot中的使用对理解代码是至关重要的。你可以看到这些example sketches:
- libraries/AP_GPS/examples/GPS_AUTO_test
- libraries/AP_InertialSensor/examples/INS_generic
- libraries/AP_Compass/examples/AP_Compass_test
- libraries/AP_Baro/examples/BARO_generic
- libraries/AP_AHRS/examples/AHRS_Test
- 每个使用AP_HAL特性的文件都需要声明一个hal引用,hal的实体在AP_HAL_XXX库里,最常用的hal函数:
- hal.console->printf() and hal.console->printf_P() to print strings (use the _P to use less memory on AVR)
- hal.scheduler->millis() and hal.scheduler->micros() to get the time since boot
- hal.scheduler->delay() and hal.scheduler->delay_microseconds() to sleep for a short time
- hal.gpio->pinMode(), hal.gpio->read() and hal.gpio->write() for accessing GPIO pins
- I2C access via hal.i2c
- SPI access via hal.spi
- setup()函数在板子启动的时候被调用一次,它实际的调用来自每块板子的HAL,所有main函数是在HAL里的,其后就是loop()函数的调用,sketch的主要工作体现在loop()函数里,注意这两个函数只是冰山一角;
AP_HAL_MAIN()
是一个HAL宏,用来产生必要的代码声明C++主要函数,以及一些板级的初始化代码,位于AP_HAL_XXX_Main.h
。
4、理解ArduPilot线程:APM1 and APM2不支持线程,所以要做一个简单的定时器和回调;有很多您需要了解的ArduPilot线程相关的关键概念:
定时器回调函数:在AP_HAL里每个平台提供了一个1 khz 计时器;可以这样注册定时器回调函数(来自MS5611 barometer driver):
1
hal.scheduler->register_timer_process(AP_HAL_MEMBERPROC(&AP_Baro_MS5611::_update));
HAL特定线程:创建一些线程支持基本的操作,这些线程提供了一种调度慢任务而不打断主飞行任务的方法,比如px4上的一些(用USB连接pixhawk,波特率为57600,命令ps,能看到一些固有的线程):
- The UART thread, for reading and writing UARTs (and USB)
- The timer thread, which supports the 1kHz timer functionality described above
- The IO thread, which supports writing to the microSD card, EEPROM and FRAM
驱动程序特定线程:例程见AP_HAL_Linux/ToneAlarmDriver.cpp
ArduPilot驱动和不同平台的驱动:举个例子,MPU6000传感器,non-PX4 平台使用AP_InertialSensor_MPU6000.cpp驱动,而PX4平台使用AP_InertialSensor_PX4.cpp驱动。
平台特定的线程和任务:在某些平台,一些基本的任务和线程将在开机的时候被创建。如PX4:
- idle task – called when there is nothing else to run
- init – used to start up the system
- px4io – handle the communication with the PX4IO co-processor
- hpwork – handle thread based PX4 drivers (mainly I2C drivers)
- lpwork – handle thread based low priority work (eg. IO)
- fmuservo – handle talking to the auxillary PWM outputs on the FMU
uavcan – handle the uavcan CANBUS protocol
它们是由 rc.APM script脚本创建,在启动的时候执行,一个学习启动更加有效的方法是无SD卡启动,因为rcS script是在rc.APM之前执行的。可以用pixhawk启动后在nsh里做如下的练习:
tone_alarm stop uorb start mpu6000 start mpu6000 info mpu6000 test mount -t binfs /dev/null /bin ls /bin perf
这些的源代码在PX4Firmware/src/drivers里,如果你看了mpu6000驱动,你会看到这么一行:
hrt_call_every(&_call, 1000, _call_interval, (hrt_callout)&MPU6000::measure_trampoline, this);
它跟AP_HAL里的hal.scheduler->register_timer_process()是等效的,用在操作迅速的常规事件里,如SPI设备驱动。或者你还可以看到hmc5883驱动里的
work_queue(HPWORK, &_work, (worker_t)&HMC5883::cycle_trampoline, this, 1);
而这个适用于速度慢一点的设备,比如IIC。
AP_Scheduler系统:AP_Scheduler 库的作用是在主线程里面划分时间片;可以通过例子 AP_Scheduler/examples/Scheduler_test.cpp学习,这个文件里边有一个表单:
1
2
3
4
5static const AP_Scheduler::Task scheduler_tasks[] PROGMEM = {
{ ins_update, 1, 1000 },
{ one_hz_print, 50, 1000 },
{ five_second_call, 250, 1800 },
};函数后面的第一个数字是调用频率,它的单位由ins.init()调用控制,这个例子使用的是RATE_50HZ,即20ms,所以ins_update()调用是20ms一次,one_hz_print()调用是1s一次,five_second_call为5s一次。第三个数字是函数预计花费的最长时间。另一个关键点是 ins.wait_for_sample()函数的调用,它是ArduPilot驱动调度的节拍器,它阻塞主函数的执行直到一个新的IMU采集的到来,而阻塞的时间是由ins.init()控制的。 AP_Scheduler tables必须有如下的属性:
- 它们不能被阻塞,除非ins.update()被调用;
- 在飞行中不能执行睡眠函数;
它们应该有可预测的最坏情况时间;
现在可以在Scheduler_test例子里做练习了。 比如做如下的事情:
read the barometer
read the compass
read the GPS
update the AHRS and print the roll/pitch
信号量:为了防止多个线程访问一个共享的数据结构而产生冲突,这里有三种原理方法:semaphores, lockless data structures and the PX4 ORB;查看libraries/AP_Compass/AP_Compass_HMC5883.cpp里的_i2c_sem变量,自己探索它的工作原理。
无锁的数据结构:ArduPilot两个无锁的数据结构的例子:
- the _shared_data structure in libraries/AP_InertialSensor/AP_InertialSensor_MPU9250.cpp
the ring buffers used in numerous places. A good example is libraries/DataFlash/DataFlash_File.cpp
DataFlash_File中可以查看 _writebuf_head 和 _writebuf_tail两个变量。
PX4 ORB:ORB是一种从系统的一部分到系统的另一部分提供数据的方式,它在一个多线程的环境中使用了发布/订阅模型。所有的定义都在PX4Firmware/src/modules/uORB/topics;例子有AP_HAL_PX4/RCOutput.cpp里的_publish_actuators(),你将看到它订阅了一个“actuator_direct”主题,它包含了每个电调的设定速度。
另外的两种与px4驱动通信的机制为:- ioctl calls (see the examples in AP_HAL_PX4/RCOutput.cpp)
- /dev/xxx read/write calls (see _timer_tick in AP_HAL_PX4/RCOutput.cpp)
5、uart和控制台:ArduPilot HAL目前有5UARTs:
- uartA – the console (usually USB, runs MAVLink telemetry)
- uartB – the first GPS
- uartC – primary telemetry (telem1 on Pixhawk, 2nd radio on APM2)
- uartD – secondary telemetry (telem2 on Pixhawk)
- uartE – 2nd GPS
你可以任意使用,但最好是按它原来的方式,因为有现成的代码。有些UARTs有着双重角色,如SERIAL2_PROTOCOL参数使uartD用于MAVLink变为用于Frsky telemetry。可以参看这个例子 libraries/AP_HAL/examples/UART_test做练习。
每个UART有一些基本的IO函数:
- printf – formatted print
- printf_P – formatted print with progmem string (saves memory on AVR boards)
- println – print and line feed
- write – write a bunch of bytes
- read – read some bytes
- available – check if any bytes are waiting
- txspace – check how much outgoing buffer space is available
- get_flow_control – check if the UART has flow control capabilities
6、RC输入与输出:ArduPilot根据板类支持几种不同类型的RC输入:
- PPMSum – on PX4, Pixhawk, Linux and APM2
- SBUS – on PX4, Pixhawk and Linux
- Spektrum/DSM – on PX4, Pixhawk and Linux
- PWM – on APM1 and APM2
RC Override (MAVLink) – all boards
其中SBUS 和 Spektrum/DSM都是串口协议。
RC输出是ArduPilot控制伺服系统和电机,RC输出默认为50 hz PWM值,但是可以更高,通常在400hz。
AP_HAL RCInput对象(hal.rcin)
它提供目前板上收到的低级的访问通道值。例子为libraries/AP_HAL/examples/RCInput/RCInput.cpp。
AP_HAL RCOutput(hal.rcout)
hal.rcin 和 hal.rcout 对象都是低级函数,所有用户配置都是通过RC_Channel,例子在 libraries/RC_Channel/examples/RC_Channel/RC_Channel.cpp。
-
位于libraries/RC_Channel,是RC_Channel的子类,可以由用户指定附加属性。
AP_HAL::Storage:hal.storage API有三个主要的函数:
- init() to start up the storage subsystem
- read_block() to read a block of bytes
write_block() to write a block of bytes
提倡使用API,只有在使用新板子或debug的时候才用hal.storage。可用存储的大小 AP_HAL/AP_HAL_Boards.h里的宏HAL_STORAGE_SIZE定义。所以如果你想使用动态存储只能使用Posix IO。
-
详情见libraries/StorageManager/StorageManager.cpp,在板子上做测试的时候注意备份好配置文件。
-
用于板载logs;也提供API从log文件里边取回数据;例子 libraries/DataFlash/examples/DataFlash_test/DataFlash_test.cpp,或者在loop()里可以看到:
DataFlash.get_log_boundaries(log_num, start, end);
-
一个很好的例子是AP_Terrain库,其中包含地形数据;是否支持可以从 AP_HAL_Boards.h查看HAVE_OS_POSIX_IO macro,还可以定义数据的存储位置;这个操作比较耗时,特别在飞行过程中不宜使用;可以看这个例子 libraries/AP_Terrain/TerrainIO.cpp学会怎么使用Posix IO。
上位机安装
由于我使用的是Ubuntu, 所以我会想办法使Mission Planner上位机能在linux平台上运行,在pixhawk的官网上也发现了这样的文章以及谷歌搜到的文章。
这里mono版本下载,参考Install Mono on Linux安装mono
1 | ~ $ sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF |
If the .NET program does run well under Mono then running it with Mono would be a better choice. You can extract the executables from the MSI using something like 7zip.
It’s like this:
Program -> Mono (Framework) -> System
Versus
Program -> .NET (Framework) -> WINE -> System
然后下载Mission Planner的ZIP版本,解压运行即可。
1 | ~ $ wget http://ardupilot.com/wp-content/plugins/download-monitor/download.php?id=83 |
对于apm_planner2可以选择安装linux版
1 | ~ $ wget http://firmware.diydrones.com/Tools/APMPlanner/apm_planner2_latest_ubuntu_trusty64.deb |
参考文章
User Manual: http://copter.ardupilot.com/
Developer Manual: http://dev.ardupilot.com/