第一个应用程序教程(Hello Sky)

    本教程详细解释了如何创建一个新的板载应用程序,以及如何运行它。

    • Pixhawk 或者 Snapdragon兼容的自驾仪
    • PX4 工具链
    • Github 账号 (免费注册)

    第一步:文件设置

    为了更方便的管理你的个人代码和上传更新到原始代码仓库,强烈建议使用fork命令通过GIT版本控制系统得到一个新的PX4固件。

    • 在github上一个账户;
    • 登陆px4的固件托管网址,然后点击右上方的FORK按钮;
    • 点击到你fork后的站点,复制你的私人PX4代码仓库的URL地址。
    • 克隆你的代码仓库到你的硬盘中,在命令行中输入:

      对于windows用户,请参考中的介绍。例如在github创建的官方应用程序中使用fork/clone命令克隆仓库到本地硬盘中。
    • 更新px4代码包含的git子模块:运行命令行工具(在windows上运行PX4终端)

    打开你本地硬盘克隆的软件仓库的Firmware/src/examples/文件夹,查看里面的文件。

    第二步:最小的应用

    Firmware/src/examples/px4_simple_app下,创建一个新的C语言文件px4_simple_app.c。(该文件已存在,你可以直接删掉它,来完全自主编辑学习。)

    从默认头文件和main主函数开始,编辑这个文件。

    1. /****************************************************************************
    2. *
    3. * Copyright (c) 2012-2015 PX4 Development Team. All rights reserved.
    4. *
    5. * Redistribution and use in source and binary forms, with or without
    6. * modification, are permitted provided that the following conditions
    7. * are met:
    8. *
    9. * 1. Redistributions of source code must retain the above copyright
    10. * notice, this list of conditions and the following disclaimer.
    11. * 2. Redistributions in binary form must reproduce the above copyright
    12. * notice, this list of conditions and the following disclaimer in
    13. * the documentation and/or other materials provided with the
    14. * distribution.
    15. * 3. Neither the name PX4 nor the names of its contributors may be
    16. * used to endorse or promote products derived from this software
    17. * without specific prior written permission.
    18. *
    19. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    20. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    21. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
    22. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
    23. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
    24. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
    25. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
    26. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
    27. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
    28. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
    29. * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    30. * POSSIBILITY OF SUCH DAMAGE.
    31. *
    32. ****************************************************************************/
    33. /**
    34. * @file px4_simple_app.c
    35. * Minimal application example for PX4 autopilot
    36. *
    37. * @author Example User <mail@example.com>
    38. */
    39. #include <px4_config.h>
    40. #include <px4_tasks.h>
    41. #include <px4_posix.h>
    42. #include <unistd.h>
    43. #include <stdio.h>
    44. #include <poll.h>
    45. #include <string.h>
    46. #include <uORB/uORB.h>
    47. #include <uORB/topics/sensor_combined.h>
    48. #include <uORB/topics/vehicle_attitude.h>
    49. __EXPORT int px4_simple_app_main(int argc, char *argv[]);
    50. int px4_simple_app_main(int argc, char *argv[])
    51. {
    52. PX4_INFO("Hello Sky!");
    53. }

    现在应用程序已经完成并且能够运行,但还没有注册成NuttShell命令行工具。如果想要将应用程序编译到固件中,将它加到下面的模块编译列表中:

    在你的应用程序的下面的文件中添加一行:

    examples/px4_simple_app

    • Pixhawk v1/2: make px4fmu-v2_default
    • Pixhawk v3: make px4fmu-v4_default

    第四步:上传并且测试应用程序

    使能uploader然后重置开发板:

    • Pixhawk v1/2: make px4fmu-v2_default upload
    • Pixhawk v3: make px4fmu-v4_default upload

    在你重置开发板之前,会在后面打印如下的编译信息:

    Loaded firmware for X,X, waiting for the bootloader...

    一旦开发板重置成功并且应用程序上传成功,打印信息:

    1. Erase : [====================] 100.0%
    2. Program: [====================] 100.0%
    3. Verify : [====================] 100.0%
    4. [100%] Built target upload

    现在通过串口或USB连接到系统命令行终端(新手玩家第一次进行USB或者串口连接系统命令行终端,请点开上面的链接,按照要求进行系统控制台安装,安装完毕后,输入回车键打开终端(linux)或者命令行工具(windows))。点击回车键能打开shell命令行工具:

    1. nsh>

    输入“help”然后回车

    1. nsh> help
    2. help usage: help [-v] [<cmd>]
    3. [ df kill mkfifo ps sleep
    4. ? echo losetup mkrd pwd test
    5. cat exec ls mh rm umount
    6. cd exit mb mount rmdir unset
    7. cp free mkdir mv set usleep
    8. dd help mkfatfs mw sh xd
    9. Builtin Apps:
    10. reboot
    11. perf
    12. top
    13. ..
    14. px4_simple_app
    15. ..
    16. sercon
    17. serdis

    现在‘px4_simple_app’已经是一个可用的命令了。输入px4_simple_app命令然后回车:

    现在应用程序已经成功注册到系统中,能够被扩展并且运行一些有用的任务。

    第五步:读取传感器数据

    为了实现一些功能,应用程序需要读取传感器的输入然后反应到对电机或者舵机的输出中。注意在这里,PX平台真正的硬件抽象的概念在这里体现—-当硬件平台或者传感器更新,完全不需要更新你的应用程序或者更新传感器驱动程序。

    在PX4中,应用程序间发送的单独的消息叫做“topics”,在本教程中,我们关心的话题是“多传感器间的uORB消息机制”( topic)。这些消息机制使得整个系统能够同步传感器数据。

    订阅一个话题是非常迅速并且简洁的:

    1. #include <uORB/topics/sensor_combined.h>
    2. ..
    3. int sensor_sub_fd = orb_subscribe(ORB_ID(sensor_combined));

    在消息读取中加入“poll()”机制:

    1. #include <poll.h>
    2. #include <uORB/topics/sensor_combined.h>
    3. ..
    4. int sensor_sub_fd = orb_subscribe(ORB_ID(sensor_combined));
    5. /* one could wait for multiple topics with this technique, just using one here */
    6. px4_pollfd_struct_t fds[] = {
    7. { .fd = sensor_sub_fd, .events = POLLIN },
    8. };
    9. while (true) {
    10. /* wait for sensor update of 1 file descriptor for 1000 ms (1 second) */
    11. int poll_ret = px4_poll(fds, 1, 1000);
    12. ..
    13. if (fds[0].revents & POLLIN) {
    14. /* obtained data for the first file descriptor */
    15. struct sensor_combined_s raw;
    16. /* copy sensors raw data into local buffer */
    17. orb_copy(ORB_ID(sensor_combined), sensor_sub_fd, &raw);
    18. printf("[px4_simple_app] Accelerometer:\t%8.4f\t%8.4f\t%8.4f\n",
    19. (double)raw.accelerometer_m_s2[0],
    20. (double)raw.accelerometer_m_s2[1],
    21. (double)raw.accelerometer_m_s2[2]);
    22. }
    23. }

    编译应用程序:

    1. make

    第六步:测试uORB消息读取机制

    最后一步,开始你的应用程序,并且切换到后台应用。

    1. px4_simple_app &

    你的应用程序会向串口输出当前传感器的值:

    它会在输出5次数据后退出。下一篇教程中会介绍如何编写一个能通过命令行控制的后台应用。

    为了能获取到计算后的数据,下一步就是“打印”这些结果。如果我们知道某一个消息是使用mavlink协议转发给地面控制站的,我们就可以通过这个消息去查看结果。例如我们通过这个方法来获得高度信息的消息。

    接口非常简单:初始化消息的结构体,然后公告这条消息:

    1. #include <uORB/topics/vehicle_attitude.h>
    2. ..
    3. /* advertise attitude topic */
    4. struct vehicle_attitude_s att;
    5. memset(&att, 0, sizeof(att));
    6. orb_advert_t att_pub_fd = orb_advertise(ORB_ID(vehicle_attitude), &att);

    在主循环中,当消息准备好时,打印这条消息。

    1. orb_publish(ORB_ID(vehicle_attitude), att_pub_fd, &att);

    修改后的完整的示例代码如下:

    1. #include <px4_config.h>
    2. #include <px4_tasks.h>
    3. #include <px4_posix.h>
    4. #include <unistd.h>
    5. #include <stdio.h>
    6. #include <poll.h>
    7. #include <uORB/uORB.h>
    8. #include <uORB/topics/sensor_combined.h>
    9. #include <uORB/topics/vehicle_attitude.h>
    10. __EXPORT int px4_simple_app_main(int argc, char *argv[]);
    11. int px4_simple_app_main(int argc, char *argv[])
    12. {
    13. PX4_INFO("Hello Sky!");
    14. /* subscribe to sensor_combined topic */
    15. int sensor_sub_fd = orb_subscribe(ORB_ID(sensor_combined));
    16. orb_set_interval(sensor_sub_fd, 1000);
    17. /* advertise attitude topic */
    18. struct vehicle_attitude_s att;
    19. memset(&att, 0, sizeof(att));
    20. orb_advert_t att_pub = orb_advertise(ORB_ID(vehicle_attitude), &att);
    21. /* one could wait for multiple topics with this technique, just using one here */
    22. px4_pollfd_struct_t fds[] = {
    23. { .fd = sensor_sub_fd, .events = POLLIN },
    24. /* there could be more file descriptors here, in the form like:
    25. * { .fd = other_sub_fd, .events = POLLIN },
    26. */
    27. };
    28. int error_counter = 0;
    29. for (int i = 0; i < 5; i++) {
    30. /* wait for sensor update of 1 file descriptor for 1000 ms (1 second) */
    31. int poll_ret = px4_poll(fds, 1, 1000);
    32. /* handle the poll result */
    33. if (poll_ret == 0) {
    34. /* this means none of our providers is giving us data */
    35. PX4_ERR("[px4_simple_app] Got no data within a second");
    36. } else if (poll_ret < 0) {
    37. /* this is seriously bad - should be an emergency */
    38. if (error_counter < 10 || error_counter % 50 == 0) {
    39. /* use a counter to prevent flooding (and slowing us down) */
    40. PX4_ERR("[px4_simple_app] ERROR return value from poll(): %d"
    41. , poll_ret);
    42. }
    43. error_counter++;
    44. } else {
    45. if (fds[0].revents & POLLIN) {
    46. /* obtained data for the first file descriptor */
    47. struct sensor_combined_s raw;
    48. /* copy sensors raw data into local buffer */
    49. orb_copy(ORB_ID(sensor_combined), sensor_sub_fd, &raw);
    50. PX4_WARN("[px4_simple_app] Accelerometer:\t%8.4f\t%8.4f\t%8.4f",
    51. (double)raw.accelerometer_m_s2[0],
    52. (double)raw.accelerometer_m_s2[1],
    53. (double)raw.accelerometer_m_s2[2]);
    54. /* set att and publish this information for other apps */
    55. att.roll = raw.accelerometer_m_s2[0];
    56. att.pitch = raw.accelerometer_m_s2[1];
    57. att.yaw = raw.accelerometer_m_s2[2];
    58. orb_publish(ORB_ID(vehicle_attitude), att_pub, &att);
    59. }
    60. /* there could be more file descriptors here, in the form like:
    61. * if (fds[1..n].revents & POLLIN) {}
    62. */
    63. }
    64. }
    65. PX4_INFO("exiting");
    66. return 0;

    第八步:运行整个示例

    运行你的应用程序:

    1. px4_simple_app

    如果打开QGroundControl,你就能通过实时绘图程序(Tools -> Analyze)来获得实时的传感器数据。
    If you start QGroundControl, you can check the sensor values in the realtime plot (Tools -> Analyze)

    小结