在Microzed上移植ROS—基于linaro
最近想玩玩智能小车,就想在吃灰已久的Microzed上面运行ROS以实现一些比较好玩的功能。第一个拦路虎就是如何把ROS移植到系统上面。Microzed目前支持比较好的Linux系统是Linaro,而ROS官方的Ubuntu ARM install of ROS Indigo(http://wiki.ros.org/indigo/Installation/UbuntuARM)并不合适,试了很多次,踩了很多坑始终无法解决依赖。本文记录一下踩过的坑,方便以后查看。
在Microzed上移植ROS—基于linaro
背景
最近想玩玩智能小车,就想在吃灰已久的Microzed上面运行ROS以实现一些比较好玩的功能。第一个拦路虎就是如何把ROS移植到系统上面。Microzed目前支持比较好的Linux系统是Linaro,而ROS官方的Ubuntu ARM install of ROS Indigo(http://wiki.ros.org/indigo/Installation/UbuntuARM)并不合适,试了很多次,踩了很多坑始终无法解决依赖。本文记录一下踩过的坑,方便以后查看。
第一步:安装好合适的linux
这一步直接参考官方教程:Installing Linux on the MicroZed(https://github.com/SDU-Embedded/linux_zynq/wiki/Installing-Linux-on-the-MicroZed).
编译环境如下:
-
主机:Windows 10 x64
-
虚拟机:ubuntu 16.04 LTS
-
开发环境:Vivado 2018.1/SDK(运行在虚拟机里,webpack版本)
按照链接的教程生产BOOT.bin
-
生成devicetree.dtb的指令有两个”O”,但绝对不是0
dtc -I dts -O dtb -o devicetree.dtb devicetree.dts
BOOT部分生成之后下载Linaro的最新版本,解压到ROOT_FS内即可
http://releases.linaro.org/debian/images/developer-armhf/latest/
第二步:让Linaro运行起来
给mirozed通电,Windows上用putty 通过串口连接,运行Linaro
注意要插上网线,dhcp模式自动获取到IP并连接到网络
运行一次:
$ apt-get update
-
如何在Windows和Mirozed板子之间传输文件?
推荐使用WinS C P连接并传输文件
-
Linaro默认不允许root登陆ssh
SCP协议是基于SSH的,当Microzed获取到IP之后就可以通过WinSCP去连接板子了,但问题是此时板子上的SSH还不允许root用户登陆,
需要做如下操作:
sudo
vi /etc/ssh/sshd_config
修改ssh服务配置文件,两种情况:
调整PermitRootLogin参数值为yes,如下图:
同时做如下修改:
1) 将PermitEmptyPasswords yes前面的#号去掉
2) 将PermitEmptyPasswords 参数值修改为yes
保存后执行:
service sshd restart # 或者
重启sshd服务即可通过WinSCP连接(使用SCP)
第三步:在Linaro上安装ROS
经过多次调试确认,ROS的ubuntu ARM的Wiki(http://wiki.ros.org/indigo/Installation/UbuntuARM)只适用于Ubuntu 14.04 armhf版本,并不适用与其他发行版,因此需要想办法从源码安装,这里直接用microzed自己编译了源码,费时较长,感兴趣的小伙伴可以试试交叉编译。
按照官网的Installing from source(http://wiki.ros.org/Installation/Source)来实现移植。
由于Linaro是基于Debian的,因此按照Debian的教程来就可以了。
-
依赖项
Bootstrap依赖
apt-get install python-rosdep python-rosinstall-generator python-wstool python-rosinstall build-essential
初始化Rosdep
$ sudo rosdep init
$ rosdep update
-
安装
创建catkin 工作目录
$ mkdir ~/ros_catkin_ws
$ cd ~/ros_catkin_ws
安装ROS-Comm: (Bare Bones),无GUI版本
$ rosinstall_generator ros_comm --rosdistro melodic --deps --tar > melodic-ros_comm.rosinstall
$ wstool init -j8 src melodic-ros_comm.rosinstall
解析依赖并安装
$ rosdep install --from-paths src --ignore-src --rosdistro melodic -y
Build catkin workspace
$ ./src/catkin/bin/catkin_make_isolated --install -DCMAKE_BUILD_TYPE=Release
取决于网络快慢和运行速度,这个过程大概在2个小时。
编译完成后执行:
$ source ~/ros_catkin_ws/install_isolated/setup.bash
此时ROS已经完成了安装
如何验证ROS?
-
Hello world—第一个Talker和listener实例
参考博文:https://blog.csdn.net/eliot_shao/article/details/73730373
-
创建一个工作区(workspace)
工作区可以作为一个独立的项目进行编译,存放ROS程序的源文件、编译文件和执行文件。建立工作区的方法如下:
[plain] view plain copy
-
$ mkdir -p ~/catkin_ws/src
-
$ cd ~/catkin_ws/src
-
$ catkin_init_workspace
虽然这时候工作区是空的,但是我们依然可以进行编译:
[plain] view plain copy
-
$ cd ~/catkin_ws/
-
$ catkin_make
这时候,会在当前文件夹下生成devel,build这两个子文件夹,在devel文件夹下能看到几个setup.*sh文件。
接下来把工作区在bash中注册
[plain] view plain copy
-
$ source devel/setup.bash
-
创建一个ROS工程包(Package)
在一个工作区内,可能会包含多个ROS工程包。而最基本ROS工程包中会包括CmakeLists.txt和Package.xml这两个文件,其中Package.xml中主要包含本项目信息和各种依赖(depends),而CmakeLists.txt中包含了如何编译和安装代码的信息。
首先切换到工作区:
[plain] view plain copy
-
$ cd ~/catkin_ws/src
现在可以使用catkin_create_pkg命令去创建一个叫beginner_tutorials的包,这个包依靠std_msgs、roscpp、rospy。
[plain] view plain copy
-
$ catkin_create_pkg beginner_tutorials std_msgs rospy roscpp
接下来在工作区编译这个工程包。
[plain] view plain copy
-
$ cd ~/catkin_ws
-
$ catkin_make
在beginner_tutorials/src下放入两个cpp文件:talker.cpp/listener.cpp
内容如下:
Talker.cpp
-
#include “ros/ros.h”
-
#include “std_msgs/String.h”
-
#include <sstream>
-
int main(int argc, char **argv)
-
{
-
/**
-
* The ros::init() function needs to see argc and argv so that it can perform
-
* any ROS arguments and name remapping that were provided at the command line. For programmatic
-
* remappings you can use a different version of init() which takes remappings
-
* directly, but for most command-line programs, passing argc and argv is the easiest
-
* way to do it. The third argument to init() is the name of the node.
-
*
-
* You must call one of the versions of ros::init() before using any other
-
* part of the ROS system.
-
*/
-
ros::init(argc, argv, “talker”);
-
/**
-
* NodeHandle is the main access point to communications with the ROS system.
-
* The first NodeHandle constructed will fully initialize this node, and the last
-
* NodeHandle destructed will close down the node.
-
*/
-
ros::NodeHandle n;
-
/**
-
* The advertise() function is how you tell ROS that you want to
-
* publish on a given topic name. This invokes a call to the ROS
-
* master node, which keeps a registry of who is publishing and who
-
* is subscribing. After this advertise() call is made, the master
-
* node will notify anyone who is trying to subscribe to this topic name,
-
* and they will in turn negotiate a peer-to-peer connection with this
-
* node. advertise() returns a Publisher object which allows you to
-
* publish messages on that topic through a call to publish(). Once
-
* all copies of the returned Publisher object are destroyed, the topic
-
* will be automatically unadvertised.
-
*
-
* The second parameter to advertise() is the size of the message queue
-
* used for publishing messages. If messages are published more quickly
-
* than we can send them, the number here specifies how many messages to
-
* buffer up before throwing some away.
-
*/
-
ros::Publisher chatter_pub = n.advertise<std_msgs::String>(“chatter”, 1000);
-
ros::Rate loop_rate(10);
-
/**
-
* A count of how many messages we have sent. This is used to create
-
* a unique string for each message.
-
*/
-
int count = 0;
-
while (ros::ok())
-
{
-
/**
-
* This is a message object. You stuff it with data, and then publish it.
-
*/
-
std_msgs::String msg;
-
std::stringstream ss;
-
ss << “hello world “ << count;
-
msg.data = ss.str();
-
ROS_INFO(“%s”, msg.data.c_str());
-
/**
-
* The publish() function is how you send messages. The parameter
-
* is the message object. The type of this object must agree with the type
-
* given as a template parameter to the advertise<>() call, as was done
-
* in the constructor above.
-
*/
-
chatter_pub.publish(msg);
-
ros::spinOnce();
-
loop_rate.sleep();
-
++count;
-
}
-
return 0;
-
}
Listener.cpp
-
#include “ros/ros.h”
-
#include “std_msgs/String.h”
-
/**
-
* This tutorial demonstrates simple receipt of messages over the ROS system.
-
*/
-
void chatterCallback(const std_msgs::String::ConstPtr& msg)
-
{
-
ROS_INFO(“I heard: [%s]”, msg->data.c_str());
-
}
-
int main(int argc, char **argv)
-
{
-
/**
-
* The ros::init() function needs to see argc and argv so that it can perform
-
* any ROS arguments and name remapping that were provided at the command line. For programmatic
-
* remappings you can use a different version of init() which takes remappings
-
* directly, but for most command-line programs, passing argc and argv is the easiest
-
* way to do it. The third argument to init() is the name of the node.
-
*
-
* You must call one of the versions of ros::init() before using any other
-
* part of the ROS system.
-
*/
-
ros::init(argc, argv, “listener”);
-
/**
-
* NodeHandle is the main access point to communications with the ROS system.
-
* The first NodeHandle constructed will fully initialize this node, and the last
-
* NodeHandle destructed will close down the node.
-
*/
-
ros::NodeHandle n;
-
/**
-
* The subscribe() call is how you tell ROS that you want to receive messages
-
* on a given topic. This invokes a call to the ROS
-
* master node, which keeps a registry of who is publishing and who
-
* is subscribing. Messages are passed to a callback function, here
-
* called chatterCallback. subscribe() returns a Subscriber object that you
-
* must hold on to until you want to unsubscribe. When all copies of the Subscriber
-
* object go out of scope, this callback will automatically be unsubscribed from
-
* this topic.
-
*
-
* The second parameter to the subscribe() function is the size of the message
-
* queue. If messages are arriving faster than they are being processed, this
-
* is the number of messages that will be buffered up before beginning to throw
-
* away the oldest ones.
-
*/
-
ros::Subscriber sub = n.subscribe(“chatter”, 1000, chatterCallback);
-
/**
-
* ros::spin() will enter a loop, pumping callbacks. With this version, all
-
* callbacks will be called from within this thread (the main one). ros::spin()
-
* will exit when Ctrl-C is pressed, or the node is shutdown by the master.
-
*/
-
ros::spin();
-
return 0;
-
}
在CMakeLists.txt中加入信息,告诉编译器需要编译的文件以及依赖,在文件末尾处添加以下信息:
-
include_directories(include ${catkin_INCLUDE_DIRS})
-
add_executable(talker src/talker.cpp)
-
target_link_libraries(talker ${catkin_LIBRARIES})
-
add_dependencies(talker beginner_tutorials_generate_messages_cpp)
-
add_executable(listener src/listener.cpp)
-
target_link_libraries(listener ${catkin_LIBRARIES})
-
add_dependencies(listener beginner_tutorials_generate_messages_cpp)
将目录切换到工作区目录,并执行catkin_make运行命令:
-
$ cd ~/catkin_ws
-
$ catkin_make
正常情况下应该编译完成
-
验证程序正常运行
后台启动roscore
分别开两个terminal运行:
-
$ cd ~/catkin_ws
-
$ source ./devel/setup.bash
-
$ rosrun beginner_tutorials talker
-
$ rosrun beginner_tutorials listener
效果如下:
Listener:
Talker:
ROScore:
帖子挺好,谢谢
写得很详细,记录了整个过程和其中的坑,学习了,谢谢