【ROS】搭建ros从x86到nvidia arm的交叉编译环境

方法1:

交叉编译环境搭建流程

  • 交叉编译环境搭建的主要思路是利用catkin提供的rostoolchain脚本设置相应的交叉编译工具、lib库地址等,中间遇到问题再针对性解决。

基础环境:

  • 目标运行环境
    • arm64 cpu
    • Ubuntu 16.04
    • ros kinetic for arm
  • 编译环境
    • x86_64 cpu
    • Ubuntu Kylin(16.04)
    • ros kinetic for amd64

环境设置流程:

1 ARM ubuntu环境上安装arm-indigo版本,后面需要将这个arm版本copy到交叉编译环境上
2 x86环境上安装docker(ubuntu)
3 下载docker-ros(ubuntu)镜像, 加载启动docker-ros(ubuntu+ROS)镜像
4 安装 sudo apt-get install g++-arm-linux-gnueabihf
5 将docker环境中中x86版本的indigo替换成arm版本的indigo,
替换之前需要备份x86版本indigo的lib,因为这些库文件在运行ros时是需要的。
将x86版本indigo的lib保存到localpath目录下,设置export LD_LIBRARY_PATH=localpath,
实现运行ros命令时使用x86的lib库。
6 将arm环境下的库文件/头文件 copy到docker环境中,目录可以自己设定比如:/$ENV{HOME}/cross-compiling/ubuntu-rootfs
7 配置rostoolchain.cmake文件

#File rostoolchain.cmake



set(CMAKE_SYSTEM_NAME Linux)

set(CMAKE_C_COMPILER /usr/bin/arm-linux-gnueabihf-gcc)

set(CMAKE_CXX_COMPILER /usr/bin/arm-linux-gnueabihf-g++)

set(CMAKE_FIND_ROOT_PATH /opt/ros/indigo $ENV{HOME}/cross-compiling/ubuntu-rootfs)



set(CMAKE_LIBRARY_PATH $ENV{HOME}/cross-compiling/ubuntu-rootfs/usr/lib/arm-linux-gnueabihf $ENV{HOME}/cross-compiling/ubuntu-rootfs/usr/lib $ENV{HOME}/cross-compiling/ubuntu-rootfs/lib $ENV{HOME}/cross-compiling/ubuntu-rootfs/usr/local/lib)



set(CMAKE_INCLUDE_PATH $ENV{HOME}/cross-compiling/ubuntu-rootfs/usr/include $ENV{HOME}/cross-compiling/ubuntu-rootfs/usr/local/include)

set(LD_LIBRARY_PATH $ENV{HOME}/cross-compiling/ubuntu-rootfs/usr/lib/arm-linux-gnueabihf $ENV{HOME}/cross-compiling/ubuntu-rootfs/usr/lib $ENV{HOME}/cross-compiling/ubuntu-rootfs/lib $ENV{HOME}/cross-compiling/ubuntu-rootfs/usr/local/lib)



set(PYTHON_EXECUTABLE /usr/bin/python)

set(PCL_ROOT $ENV{HOME}/cross-compiling/ubuntu-rootfs/usr)



set(CMAKE_CROSSCOMPILING true)

message("${CMAKE_CROSSCOMPILING}")



# Have to set this one to BOTH, to allow CMake to find rospack



set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY)

set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)

set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)

8 修改/opt/ros/indigo/share/catkin/cmake/tools/rt.cmake

该文件的第34行

if(NOT (APPLE OR WIN32 OR MINGW OR ANDROID))修改为if(NOT (APPLE OR WIN32 OR MINGW OR ANDROID OR UNIX))

9 个人理解cmake交叉编译过程就是修改cmake搜索链接库文件位置的过程,正常编译会搜索x86库文件做在位置,
交叉编译需要将搜索位置切换到目标架构需要的库文件存放位置,比如/$ENV{HOME}/cross-compiling/ubuntu-rootfs
实现方法:
修改catkin_INCLUDE_DIRS catkin_LIBRARY_DIRS,将这两个变量中指向x86系统的位置,修改为指向交叉编译依赖文件存放位置

/opt/ros/indigo/share/catkin/cmake/catkinConfig.cmake


这个文件末增加如下处理:

list(APPEND catkin_INCLUDE_DIRS ${CMAKE_INCLUDE_PATH})

list(APPEND catkin_LIBRARY_DIRS ${CMAKE_LIBRARY_PATH})



set(catkin_LIBRARIES_tmp ${catkin_LIBRARIES})

set(catkin_LIBRARIES "")

foreach(catkinlib ${catkin_LIBRARIES_tmp})

string(LENGTH ${catkinlib} lengths)

if (${lengths} GREATER 12)

string(SUBSTRING ${catkinlib} "/" 12 result)

string(COMPARE EQUAL ${result} "/usr/lib/lib" compareResult)

if (${compareResult})

string(REPLACE "/usr/lib/lib" "$ENV{HOME}/cross-compiling/ubuntu-rootfs/usr/lib/lib" result_tem ${catkinlib})

list(APPEND catkin_LIBRARIES ${result_tem})

else()

list(APPEND catkin_LIBRARIES ${catkinlib})

endif()

else()

list(APPEND catkin_LIBRARIES ${catkinlib})

endif()

endforeach()

10 编译命令

catkin_make -DCMAKE_TOOLCHAIN_FILE=/$filepath/rostoolchain.cmake

编译

  • 命令
catkin_make -DCMAKE_TOOLCHAIN_FILE=/$filepath/rostoolchain.cmake

验证编译产出

  • 将devel产出拷贝到px2的workspace中,启动roscore之后,进入devel/lib/hdmap_interface,运行./hdmap_interface_node

tip:参考了https://zhuanlan.zhihu.com/p/183819313的相关操作,并清晰阐释了该流程。

方法2:

ros开源了一套编译库,直接通过python即可得到交叉编译的结果。

这个包安装ros_cross_compile命令后,使用以下指令即可实现交叉。命令的第一个参数是指向ROS工作区的路径。

下面是一个标准工作流的简单调用

python -m ros_cross_compile /path/to/my/workspace --arch aarch64 --os ubuntu --rosdistro dashing

关于脚本的其他参数说明,执行"python -m ros_cross_compile -h"获得。

tips:python后面需要加 -m 来实现模块当作脚本来启动,作者发现文章举的例子较为详细,因此作为采用,来阐述此处为何需要python -m

python xxx.pypython -m xxx
这是两种加载py文件的方式:
前者叫做直接运行,此时脚本的__name__'main'
后者把模块当作脚本来启动,相当于import,此时脚本的__name__'xxx'。这种启动方式时最好不要带后缀.py

不同的加载py文件的方式,主要是影响sys.path这个属性。sys.path是一个list,是当前Python解析器运行的环境,Python解析器会在这些目录下去寻找依赖库。python导入文件或模块时默认会先在sys.path里找其的路径。

# 直接启动:python run.py
test_import_project git:(master) ✗ python run.py
['/Users/sx/Documents/note/test_py/test_import_project',  
 '/usr/local/Cellar/python/2.7.11/Frameworks/Python.framework/Versions/2.7/lib/python27.zip',  
  ...]
# 以模块方式启动:python -m run
test_import_project git:(master) ✗ python -m run
['',  
 '/usr/local/Cellar/python/2.7.11/Frameworks/Python.framework/Versions/2.7/lib/python27.zip',

对比一下就可以发现,-m运行比直接运行仅是多了一个’’,也就是当前所在的目录。
直接启动:把run.py文件所在的目录放到了sys.path属性中。
模块启动:把也就是当前路径放到了sys.path属性中

在实际场景中,对于多个启动目录的程序效果明显

# 目录结构如下
package/
    __init__.py
    mod1.py
package2/
    __init__.py
    run.py  
# run.py 内容如下
import sys
from package import mod1
print(sys.path)

现在就尝试以两种不同的方法启动run.py文件。

# 直接启动(失败)
➜  test_import_project git:(master) ✗ python package2/run.py
Traceback (most recent call last):
  File "package2/run.py", line 2, in <module>
    from package import mod1
ImportError: No module named package

# 以模块方式启动(成功)
➜  test_import_project git:(master) ✗ python -m package2.run
['',
'/usr/local/Cellar/python/2.7.11/Frameworks/Python.framework/Versions/2.7/lib/python27.zip',
...]

接下来分析一下原因。
需要注意的是,当需要启动的py文件引用了一个模块,在启动的时候需要考虑sys.path中有没有import的模块的路径。这个时候,到底是使用直接启动,还是以模块的启动?目的就是把import的那个模块的路径放到sys.path中。
导入一个模块时,解释器先在当前目录中搜索它。如果没有找到的话,就会到 sys.path变量中给出的目录列表中查找

我们要启动package2文件夹下的run.py,所需模块在package目录中。因此当我们直接执行run.py时,只是把run.py文件所在的目录放到了sys.path属性中,sys.path并没有把package目录收录。故会报错
当我们使用-m参数时,把当前路径放到了sys.path属性中。当前路径包含了package目录与package2目录,故均在sys.path中。运行成功。

因此此处使用python -m能够有效地去管理交叉编译的问题,可以使ROS环境包与交叉编译包能够同处于一个目录下,方便编译。

同时为了进一步方便代码的debug,本文还提供了pycharm如何设置.py作为脚本启动,即带-m参数运行

首先是如何以python xxx.py方式直接运行:
Edit Configurations选项中,选择的是Script path。输入框是run.py的绝对路径。

如何以python -m xxx方式运行,将xxx.py作为模块运行(感谢stackoverflow):
Edit Configurations选项中,点击Script path输入框左侧的倒三角,选择Module name,右边输入框输入xxx,即模块的名字,不带.py后缀。然后就可以在Parameters设置任何参数。这种方法不需要在任何地方包含 -m 参数。

通过该类方法能够有效地去管理代码的读取与存储,当然不同的芯片有可能具有不同的使用方法,目前该方法二仍然只适用于ubuntu与Debain系统,如果以传统的ARM系统,有可能仍然需要使用方法1来保证安全稳定。此处提供一个古月的交叉编译博客,而RK3308等RK系列也得到了众多开发者的支持。而树莓派等虽然存在编译界面,但是由于性能等问题,很多人还是尝试将其进行交叉编译以及移植。由于本人最近才接触交叉编译,有些不合适的地方请各位批评指正。

在这里插入图片描述