标签 C 下的文章

源创会开源访谈:十年成长,Go语言的演化之路

在参加源创会沈阳站分享之前,接受了开源中国社区编辑王练的文字专访,以下是我针对专访稿的内容。

同时该专访稿首发于开源中国开源访谈栏目,大家可以点击这里看到首发原稿。

1、首先请介绍一下自己

大家好!我叫白明(Tony Bai),目前是东软云科技的一名架构师,专职于服务端开发,日常工作主要使用Go语言。我算是国内较早接触Go语言的程序员兼Advocater了,平时在我的博客微博和微信公众号”iamtonybai”上经常发表一些关于Go语言的文章和Go生态圈内的信息。

在接触Go之前,我主要使用C语言开发电信领域的一些后端服务系统,拥有多年的电信领域产品研发和技术管理经验。我个人比较喜换钻研和分享技术,是《七周七语言》一书的译者之一,并且坚持写技术博客十余年。同时我也算是一个开源爱好者,也在github上分享过自己开发的几个小工具。

目前的主要研究和关注的领域包括:Go、KubernetesDocker区块链和儿童编程教育等。

img{512x368}

2、最初是因为什么接触和使用 Go 语言的?它哪方面的特性吸引了您?

个人赶脚:选编程语言和谈恋爱有些像(虽然我只谈过一次^_^),我个人倾向一见钟情。我个人用的最多的编程语言是GoC,这两门语言算是我在不同时期的“一见钟情”的对象吧,也是最终“领(使)证(用)”的,前提:编程世界是“一夫多妻制”^0^。

当然早期也深入过C++,后来JavaRubyCommon LispHaskellPython均有涉猎,这些语言算是恋爱对象,但最终都分手了。

最初接触到Go应该是2011年,那是因为看了Rob Pike的3 Day Go Course,那时Go 1.0版本还没有发布,如果没记错,Rob Pike slide中用的还是Go r60版本的语法。现在大脑中留存的当时的第一感觉就是“一见钟情”!

现在回想起来,大致有这么几点原因:

  • Go与C一脉相承,对于出身C程序员的我来说,这一语言传承非常自然,多体现在语法上;
  • Go语言非常简单,尤其是GC、并发goroutine、interface,让我眼前一亮;
  • Rob Pike的Go Course Slide组织的非常好,看完三篇Slide,基本就入门了。

于是在那之后,又系统阅读了Ivo Balbaert的《The Way To Go》、《Programming in Go – Creating Applications for the 21st Century》等基本新鲜出炉的书,于是就走入了Go语言世界。

不过当时Go1尚未发布,Go自身也有较大变化,工作中也无法引入这门语言,2013年对Go的关注有些中断,2014年又恢复,直至今天。现在感觉到:如果工作语言与兴趣语言能保持一致是多么幸福的一件事啊。

3、有人说 Go 是互联网时代的 C 语言,对于这两门语言,您是怎么看的?

如果没记错,至少在国内,第一个提出这种观点的是现七牛的ceo许式伟了,老许是国内第一的Go 鼓吹者,名副其实;而且许式伟的鼓吹不仅停留在嘴上,更是付诸于实践:据说其七牛云的基础设施基本都是Go开发的。因此,对他的“远见卓识”还是钦佩之至的。

C语言缔造的软件行业的成就是举世瞩目,也是公认的。其作者Dennis Ritchie授予图灵奖就是对C语言最大的肯定和褒奖。C语言缔造了单机操作系统和基础软件的时代:UnixLinux、nginx/apache以及无数以*inx世界为中心的工具,是云时代之前最伟大的系统编程语言和基础设施语言。

至于 “Go是互联网时代的 C 语言”这一观点,如果在几年前很多人还会疑惑甚至不懈,但现在来看:事实胜于雄辩。我们来看看当前CNCF基金会(Cloud Native Computing Foundation)管理的项目中,有一大半都是Go语言开发的,包括KubernetesPrometheus等炙手可热的项目;这还不包括近两年最火的docker项目。事实证明:Go已成为互联网时代、云时代基础设施领域、云服务领域的最具竞争力的编程语言之一。

不过和C不同的是,Go语言还在发展,还在演进,还有巨大的提升空间,Gopher群体还在变大,去年再次成为Tiboe的年度语言就是例证。

当然我们还得辩证的看,Go语言虽然在云时代基础设施领域逐渐继承C语言的衣钵,但是由于语言设计理念和设计哲学上的原因,在操作系统以及嵌入式领域,Go还在努力提升。

4、Go 也经常被拿来和 Java、Rust 等语言比较,您认为它最适合的使用场景有哪些?

早期对Java有所涉猎,但止步于Java体量过重和框架过多;Rust和Go一样是近几年才兴起的一门很有理想、很有抱负的编程语言,其目标就是安全的系统级编程语言,运行性能极佳,用以替代C/C++的,但就像前面所提到的那样,第一眼看到Rust的语法,就没有那种“一见钟情”的赶脚,希望Rust不要像C++那样,演变的那么复杂。

Go从其第一封设计email出炉到如今已有十年了,我觉得也不应该由我来告诉大家Go更适合应用在什么领域了,事实摆在那里:“大家都用的地方,总是对的”。这里我只是大致归纳一下:

Go在数据科学、人工智能领域也有较大进展,希望在将来能看到Go在这些领域有杀手级项目出现。

5、Go发展已有10 年,其特性随着版本的迭代不断在更新,您觉得它最好的和最需要改进的特性分别有哪些?

每种语言都有自己的设计哲学和设计者的考量。我在GopherChina 2017的topic中就提到过Go语言的价值观,其中之一就是Simplicity,即简单。相信简单也是让很多开发者走进Gopher世界的重要原因。从今年GopherCon 2017大会上Russ Cox的“Toward Go 2”的主题演讲中,我们也可以看出:Go team并不会单纯地为了迎合community的意愿去堆砌feature,那go势必走上c++的老路,变得日益复杂,Go受欢迎的基础之一就不存在了。

但演进就一定会要付出代价的,尤其是Go1的约束在前。从我个人对Go的应用来看,最想看到的是包管理和error处理方面的体验提升。但我觉得这两点都是可以通过渐进改进实现的,甚至不会影响到Go1兼容性,不会像引入generics机制,实现难度也不会太高。

对于目前的error handling机制,我个人并没有太多的排斥,这可能是因为我出身C程序员的缘故吧。在error handling这块,只是希望能让gopher拥有更好的体验即可,比如说围绕现有的error机制,增加一些设施以帮助gopher更好的获取error cause信息,就像github.com/pkg/errors包那样。

对于社区呼声很高的generics(泛型),我个人倒是没有什么急切需求。generics虽然可以让大幅提升语言的表现力(expressiveness),但也给语言自身带来了较大的复杂性。就个人感受而言,C++就是在加入generics后才变得无比庞大和复杂的,同时generics还让很多C++ programmer沉溺于很多magic trick中无法自拔,这对于以“合作分工”为主流的软件开发过程来说,并不是好事情。

6、Go 官方团队已发布 2.0 计划,更侧重于兼容性和规模化方面。对此,您怎么理解?Go 否已达到最佳性能?

这个问题和上面的问题有些类似,我的想法差不多。Go team在特性演进方面会十分谨慎,这也是go Team一贯的风格。从Go1到Go2,从现在看来,这个时间跨度不会很短,也许是2-3年也不一定,心急吃不了热豆腐^0^,社区分裂可不是go team想看到的事情,python可是前车之鉴。

另外,Go性能显然还是有改善空间的,尤其是编译性能、GC吞吐和延迟的tradeoff方面;另外goroutine调度器算法方面可能还有改进空间。当前Goroutine调度算法的实现者Dmitry Vyukov之前就编写了一个scheduler优化的proposal: NUMA-aware scheduler for Go(针对numa体系的优化),但也许因为重要性、优先级等考量,一直没有实现,也许后续会实现。

7、Go 在国内似乎比国外还要火,您认为造成这种现象的原因是什么?

从一些搜索引擎的trend数据来看,Go在中国地区的确十分火热,甚至在热度值上是领先于欧美世界的。个人觉得造成这种现象的原因可能有如下几点:

  • 语言本身的接受度高

首先,从Go语言本身考虑。事实证明了:Go语言的设计匹配了国内程序员的行业业务需求和对语言特性的需求(口味):
a) 语言:简单、正交组合和并发;开发效率和运行效率双高;
b) 自带battery:丰富的标准库和高质量第三方库;
c) 迎合架构趋势:天生适合微服务….

  • 引入早且与Go advocator的努力分不开

当前再也不是那个“酒香不怕巷子深”的年代了,再好的编程语言也需要推广和宣称。Go team在社区建设、全世界推广方面也是不遗余力。至于国内更是有像许式伟、Astaxie这样的占据高端IT圈子的advocator在站台宣传。

  • 互联网飞速发展推动Go在国内落地

中国已经是事实的移动互联网时代的领军者,大量创业公司如雨后春笋般诞生。而Go对于startup企业来说是极其适合的。开发效率高,满足了Startup企业对产品或服务快速发布的需求;运行效率高可以让startup公司节省初期在硬件方面的投入:一台主机顶住100w并发。

对于那些巨头、大公司而言,Go又是云计算时代基础设施的代表性语言,自然也会投入到Go怀抱,比如:阿里CDN、百度门户入口、滴滴、360等。

8、对于刚开始学习 Go ,并期待将其应用在项目中的新人们,您有哪些建议?

学语言,无非实践结合理论。

  • 理论:书籍和资料

这里转一下我在知乎上一个回答

强烈推荐:Rob Pike 3-day Go Course,虽然语法过时了,但看大师的slide,收获还是蛮多的。

Go基础: Go圣经《The Go Programming Language》和《Go in Action》。
原理学习: 雨痕的《Go学习笔记》。
Go Web编程: 直接看astaxie在github上的《Go web编程》。

还有一本内容有些旧的,但个人觉得值得一看的书就是《The Way To Go》,大而全。Github上有部分章节的中译版

另外,建议看一遍官方的Language specificationeffective gogo faq,对学go、理解go设计的来龙去脉大有裨益。

  • 实践:多读多写Code

多读代码:首选标准库,因为Go的惯用法和最佳实践在标准库中都有体现。

写代码:这个如果有项目直接实践那是非常的幸福;否则可以从改写一个自己熟悉领域的工具开始。比如:以前我刚接触Go的时候,没啥可写的。就改写一套cmpp协议实现。后来做wechat接口,实现了一个简单的wechat基本协议,当然这两个代码也过于陈旧了,代码设计以及其中的go语言用法不值得大家学习了^0^。


微博:@tonybai_cn
微信公众号:iamtonybai
github.com: https://github.com/bigwhite

Hello, Apollo

要说目前哪个技术领域投资最火热,莫过于人工智能。而人工智能领域中最火的(或者说之一)肯定要算上自动驾驶。自动驾驶的概念不是什么新鲜的玩意了,只是随着近两年这一波人工智能的大热,自动驾驶又被推到了风口浪尖。各大汽车厂商、互联网公司也都跃跃欲试,准备给汽车这一“历经百年的黄金平台”做一次新的“赋能”。

今年7月5日,国内搜索引擎No.1企业百度在其首届百度AI开发者大会上发布了Apollo自动驾驶开放平台,同时百度也对外宣布baidu正式从互联网公司转型为一家人工智能公司。作为“错过了移动互联网时代”的典型公司代表,百度这次押宝人工智能,我觉得也是战略上迫不得已的选择:在现有现金牛“搜索广告业务”还能带来大量利润的时候,为抓住未来那头现金牛而进行的努力。而Apollo自动驾驶平台恰是百度人工智能战略的重要组成部分。

Apollo,阿波罗是古希腊神话中的光明之神,这个名字在西方文化中“自带光环”。提到Apollo,很多人还会想到半个多世纪前美国著名的“登月计划”。百度将其自动驾驶平台命名为Apollo,我猜测是有“借势之意”,即期望Apollo这个项目能在百度众多人工智能业务中拥有美好光明的前景。

作为技术人员,我们不能像一般媒体人员那样根据官方提供的“说辞”做宽泛的介绍,我们要与Apoll亲密接触,看看Apollo究竟是什么,究竟能做什么。这里就和大家一起来Say Hello to Apollo。

一、自动驾驶汽车- “百年黄金平台”的新时代赋能

在正式入门Apollo之前,还要说点“废话”。在接触Apollo之前,我从未认真思考过“汽车”这个平台,这次算是“顿悟”,虽然也算不上深刻。就我看来,汽车 是一个不可多得的“黄金平台”。作为一个平台,汽车已经有了上百年的历史,见证了人类科学技术的发展,是跨学科之集大成者。这百年多时间,任何新的、先进的民用技术都会赋能在汽车工业上。以一个长不足5米,重量不超过2t的一般家用乘用车为例,我们在其上面能看到先进的能源技术、材料技术、化工技术、电子技术、通讯技术以及精密的机械原件和组装技术等,可以说汽车为各个公司的创造力提供了展示的舞台。

就普通老百姓的衣食住行而言,汽车也是史无前例的高频使用典范,且是最直接、最贴近普通百姓生活的,这些都是飞机、火车等无法媲美的(如果非要选一个,那只有智能终端能与汽车媲美了,尤其是在集成度方面)。即便是到了科幻片中的漫天跑飞行器的时候,汽车也可能依旧是短距离交通的首选。当然届时的汽车很可能与我们此时的汽车大不相同了。随着时代的进步,汽车也在演化,日新月异的新技术、新材料、新能源对汽车的进一步赋能,因此汽车依旧是朝阳产业,这也是国际资本依旧积极群雄逐鹿汽车工业发展的根本原因了。比如:通过新能源方式赋能汽车的特斯拉、通过无人驾驶技术赋能的Google的waymo等。当然,不仅是从技术方面,从商业模式方面也有围绕着汽车这一平台创新的经典案例,典型的比如:uber滴滴等的高效出行以及近期日渐升温的共享汽车出行。

可以说,各大公司都在从自身优势出发,考虑如何为汽车这一百年黄金平台赋能。从这一点出发,我们就能大致理解百度Apollo的出现了:它是baidu结合自身的技术优势和数据优势拥抱汽车工业、为汽车做新时代赋能而迈出的重要一步。

二、Apollo的技术架构

Apollo是一套完整的自动驾驶技术方案,官方架构原图的截图较为模糊,这里自己画了一个简单的四层结构,每层内的模块暂未画出,因为不是本次入门的重点:

img{512x368}

按照上图,apollo自动驾驶分成四层技术栈,从下到上分别为:

1、Reference Vehicle Platform(参考车辆平台)

自动驾驶最终都要落地到车上,因此apollo抽象了一个”参考车辆平台”层,通过电子化的方式控制车辆的行驶行为。

Note: 在开发者大会上,百度展示了由美国创业公司AutonomouStuff基于Apollo 1.0开放平台改装而成的循迹自动驾驶车,这辆车是一辆美系的林肯MKZ。也就是说当前发布的Apollo适配林肯MKZ是没有问题的。但这款中型车对于普通开发者来说门槛算是稍高了。如果百度能拿出一款大众系、丰田系或至少也应该是一个本田系这样的车型,那对自动驾驶领域的开发者或者说爱好者来说,才是福利。相比而言,著名黑客George Hotz创立的自动驾驶技术公司comma.ai为其openpilot初始选用的车型则是Honda系的思域和CR-V,滥大街的车型,容易搞到,且低成本搞到,也容易改装。

2、Reference Hardware Platform(参考硬件平台)

这一层为自动驾驶汽车提供计算、感知、交互的硬件能力,包括计算单元(车载处理器设备)、GPS/IMU(惯性测量设备)、摄像头、激光雷达、声波雷达、HMI(人机接口)等。在发布的Apollo 1.0版本中,开放的硬件能力包括:计算单元、GPS/IMU(惯性测量设备)以及HMI。

3、Apollo open software Platform (开放软件平台)

这一层是百度Apollo 1.0开放的核心部分,见下图(蓝色的代表在apollo 1.0.0中已经开放的能力):

img{512x368}

从图中看到,这一层还可以分为三个子层,从下至上分别是:

  • apollo kernel层

这一层是运行于硬件上面的OS,对于自动驾驶这种实时性要求特别强的领域,这里显然只能是RTOS(实时操作系统)。Apollo 1.0开放的源码中包含一个”Apollo Kernel“的项目,在这个项目下汇集着可以满足实时性需求的OS kernel。当然目前还仅有一个选择:realtime linux kernel。这是apollo基于Linux Kernel 4.4.32+realtime patch定制的一款专用linux内核。

  • apollo platform层

在Kernel层的上面就是apollo的runtime framework了,提供platform级的支撑。Apollo 1.0同样也创建了一个专用项目:apollo-platform,用于汇集满足apollo平台级支撑需求的platform。当前该项目下也仅提供了一种选择:Apollo ROS,是基于ROS1的Indigo版二次开发后的定制版ROS。Apollo ROS基于自动驾驶需求出发,对ROS1主要做了三方面改进:

  • 为优化自动驾驶大量使用传感器引发很大的传输带宽需求, Apollo ROS改变基于socket的网络传输模式,大量采用共享内存的node间通信机制,减少传输中的数据拷贝,显著提升传输效率, 尤其是在满足一对多的传输场景下效果明显;

  • 从鲁棒性出发,使用RTPS(Real-Time Publish Subscribe)服务发现协议实现完全的P2P网络拓扑,避免原ROS的以Master作为拓扑网络的中心的单点故障问题;

  • 使用protobuf替代原ROSmessage,提供很好的向后兼容,避免接口升级后,不同版本的模块难以兼容的问题。

其实第二点改进也是ROS2正在做的事情。关于Apollo ROS的详尽变化,可以参考前不久百度工程师的一个分享:《Apollo代码开放框架—ROS 探索与实践》

  • apollo modules层

在这一层是apollo的功能modules,当前似乎依旧是基于ROS的package开发的,在github.com/ApolloAuto/apollo/modules/common/apollo_app.cc你大致能看出来一个ROS Package的开发模板。这一层提供诸如:规划(planning)、洞察(perception)、控制(control)、预测(prediction)、决策(decision)、定位等诸多功能。但Apollo 1.0仅仅开放了Control、Localization和HMI三个module,因为这三块足以构成Apollo 1.0提供的封闭场地循迹驾驶体系了。

4、Cloud Services(云端服务)

Apollo 1.0还开放了云端数据平台,以及唤醒万物的DuerOS能力。DuerOS也是Baidu人工智能战略的重要棋子,似乎也是目前Baidu在AI方面最为成熟的、应用最广的产品。当然这一层还包括仿真、高精度地图等服务,不过目前尚未开放。

三、上手Apollo

买不起林肯MKZ的童鞋也不要担心,Apollo 1.0提供了一个本地仿真工具,给你一个与Apollo亲密接触的途径,让你可以在PC上肆无忌惮地玩耍,毕竟Apollo 1.0仅提供封闭场地的寻迹能力,相对简单。

我们的重点是Apollo open software Platform这一层,而这一层中,我们不关心apollo kernel,只关心Apollo ROS和三个已经开放的apollo modules。

1、下载release版本

截至目前为止,Apollo仅发布了一个版本:apollo-v1.0.0,我们可以从github上将其下载到本地:

# wget -c https://github.com/ApolloAuto/apollo/archive/v1.0.0.tar.gz
# tar zxvf v1.0.0.tar.gz
# cd apollo-1.0.0
# ls -F
apollo_docker.sh*  apollo.doxygen  apollo.sh*  AUTHORS.md  BUILD  CPPLINT.cfg
docker/  docs/  LICENSE  modules/  README.md  scripts/  third_party/  tools/  WORKSPACE

注意:我的实验环境为ubuntu 16.04.1 amd64。

2、本地源码构建

对于基于Apollo这个framework的开发者,Apollo官方强烈建议直接采用官方预定义好的专用docker环境(for dev)。对于爱折腾的我而言,必须要在本地做一次源码构建,即使这个体验是糟糕的,甚至最终是失败的^0^。源码构建的命令很简单,一行即可:

# cd apollo-1.0.0
# bash apollo.sh build

在这个过程中,我遇到了两个错误:

  • bazel不存在

Apollo的构建依赖google出品的bazel构建工具,我个人对bazel并没有什么研究,这里先装上再说:

# echo "deb [arch=amd64] http://storage.googleapis.com/bazel-apt stable jdk1.8" |  tee /etc/apt/sources.list.d/bazel.list
deb [arch=amd64] http://storage.googleapis.com/bazel-apt stable jdk1.8

# curl https://bazel.build/bazel-release.pub.gpg | apt-key add -
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  3157  100  3157    0     0   3202      0 --:--:-- --:--:-- --:--:--  3201
OK

# apt-get update && apt-get install bazel
  • third_party/ros/setup.bash: No such file or directory

apollo的编译要依赖ros,但apollo并没有自带ros。我们需要到apollo platform那个项目中去下载Apollo ROS:

# wget -c https://github.com/ApolloAuto/apollo-platform/releases/download/1.0.0/ros-indigo-apollo-1.0.0.x86_64.tar.gz
# tar zxvf ros-indigo-apollo-1.0.0.x86_64.tar.gz
# cd ros
# ls -F
bin/  BUILD  env.sh*  etc/  include/  lib/  setup.bash  setup.sh  _setup_util.py*  setup.zsh  share/

将下载的ros目录copy到apollo-1.0.0/third_party下,并chmod +x third_party/ros/setup.bash。

我们再次执行bash apollo.sh build,这次执行前面的error和warning基本都消失了,apollo.sh脚本开始下载依赖包并编译:

# bash apollo.sh build
ROS_DISTRO was set to 'kinetic' before. Please make sure that the environment does not mix paths from different distributions.
[WARNING] ESD CAN library supplied by ESD Electronics does not exit.
[WARNING] If you need ESD CAN, please refer to third_party/can_card_library/esd_can/README.md
.
____Loading package: modules/common/util/testing
____Loading package: @com_github_grpc_grpc//
____Loading package: @google_styleguide//
____Loading package: @glog//
____Loading package: @eigen//
____Loading package: @gtest//
____Loading package: @civetweb//
____Loading package: @com_github_google_protobuf//
____Loading package: @websocketpp//
____Loading package: @curlpp//
Building on x86_64, with targets:
//tools/platforms:x86_64
//tools/platforms:aarch64
//modules/prediction:prediction
//modules/prediction:prediction_lib
... ...
//modules/common:log
//modules/canbus/proto:canbus_proto.pb
//:x86_64
//:arm64
WARNING: Running Bazel server needs to be killed, because the startup options are different.
INFO: Downloading https://github.com/google/boringssl/archive/master-with-bazel.zip via codeload.github.com: 2,750,374 bytes
INFO: Cloning https://github.com/madler/zlib: Receiving objects (3309 / 5016)
INFO: Downloading https://github.com/google/boringssl/archive/master-with-bazel.zip via codeload.github.com: 2,773,664 bytes
INFO: Cloning https://github.com/madler/zlib: Receiving objects (3314 / 5016)
INFO: Downloading https://github.com/google/boringssl/archive/master-with-bazel.zip via codeload.github.com: 2,795,584 bytes
INFO: Downloading https://github.com/google/boringssl/archive/master-with-bazel.zip via codeload.github.com: 13,504,198 bytes

INFO: Downloading https://github.com/google/boringssl/archive/master-with-bazel.zip via codeload.github.com: 13,522,008 bytes
INFO: Found 190 targets...
[34 / 41] Compiling external/com_github_google_protobuf/src/google/protobuf/compiler/java/java_message_lite.cc [for host]
[41 / 48] Compiling external/com_github_google_protobuf/src/google/protobuf/compiler/command_line_interface.cc [for host]
[157 / 163] Compiling external/com_github_google_protobuf/src/google/protobuf/compiler/javanano/javanano_enum.cc [for host]
[752 / 756] Compiling external/com_github_grpc_grpc/src/core/ext/client_config/resolver_result.c

ERROR: /root/test/apolloauto/apollo-1.0.0/modules/canbus/BUILD:32:1: Linking of rule '//modules/canbus:canbus' failed: gcc failed: error executing command /usr/bin/gcc -o bazel-out/local-dbg/bin/modules/canbus/canbus '-Wl,-rpath,$ORIGIN/../../_solib_k8/_U_S_Sthird_Uparty_Sros_Cros_Ucommon___Uthird_Uparty_Sros_Slib' ... (remaining 8 argument(s) skipped): com.google.devtools.build.lib.shell.BadExitStatusException: Process exited with status 1.
modules/canbus/main.cc:21: error: undefined reference to 'ros::init(int&, char**, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned int)'
third_party/ros/include/ros/publisher.h:107: error: undefined reference to 'ros::console::initializeLogLocation(ros::console::LogLocation*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, ros::console::levels::Level)'
... ...
collect2: error: ld returned 1 exit status
INFO: Elapsed time: 578.172s, Critical Path: 26.62s
============================
[ERROR] Build failed!
[INFO] Took 597.189 seconds
============================

经过漫长的等待后,还是以失败告终。并且C++的错误输出分析起来真是好痛苦,于是暂时放弃本地源码编译。

3、pre-specified Docker dev环境

既然apollo已经为我们准备好了pre-specified Docker dev环境,我们不妨用一下,下载和启动该环境可以用下面命令:

# cd apollo-1.0.0
# bash docker/scripts/dev_start.sh

apolloauto/apollo:dev-latest这个image超级庞大,大约有7个G左右,所以你需要耐心等待一会儿了。docker运行起来后,我们在另外一个terminal windows下可以执行下面命令切入到该docker容器内部:

# bash docker/scripts/dev_into.sh
root@myhost: /apollo#

在dev container中,我们可以来编译一下apollo源码:

root@myhost:/apollo# bash apollo.sh build
... ...
Copyright (c) 2017 Various License Holders. All Rights Reserved
Apollo software is built on top of various other open source software packages,
a complete list of licenses are located at https://github.com/ApolloAuto/apollo/blob/master/third_party/ACKNOWLEDGEMENT.txt

You agree to the terms of all the License Agreements.

Type 'y' or 'Y' to agree to the license agreement above, or type any other key to exit
y[WARNING] ESD CAN library supplied by ESD Electronics does not exit.
[WARNING] If you need ESD CAN, please refer to third_party/can_card_library/esd_can/README.md
____Loading package: modules/monitor/common
____Loading package: modules/common/adapters
____Loading package: modules/dreamview/conf
____Loading package: modules/control/integration_tests
____Loading package: @google_styleguide//
____Loading package: @com_github_google_protobuf//
... ...
[502 / 1,099] Compiling external/com_github_grpc_grpc/src/core/ext/transport/chttp2/transport/hpack_encoder.c
[914 / 1,524] Compiling external/com_github_grpc_grpc/src/core/ext/census/tracing.c
[1,304 / 1,527] Linking modules/canbus/vehicle/libmessage_manager_base.a

INFO: Elapsed time: 371.151s, Critical Path: 260.93s
============================
[ OK ] Build passed!
[INFO] Took 401.521 seconds
============================

由于dev环境中相关的依赖已经就绪,因此无需过多干预,在漫长的一段等待后,我们看到编译ok了。

4、运行apollo demo

在dev enviroment中或apollo:release-latest中,我们都可以运行apollo的一个寻迹小车的demo。以apollo:release-latest image环境为例:

// 启动基于apollo:release-latest image的apollo container(image size大约为3G,耐心等待下载):

# cd apollo-1.0.0/
# bash docker/scripts/release_start.sh

//切入到容器中去
# bash docker/scripts/release_into.sh
root@myhost:/apollo#

在容器中启动HMI(human-machine interface):

root@myhost:/apollo# bash scripts/hmi.sh
Start roscore...
HMI ros node service running at localhost:8887
HMI running at http://localhost:8887

root@myhostr:/apollo# rosnode list
/hmi_ros_node_service
/rosout

可以看到,hmi.sh脚本启动了roscore(ros master节点和相关服务)以及hmi的service,我们打开浏览器,输入:http://host_ip:8887即可看到如下场景:

img{512x368}

在容器内继续执行如下命令,回放小车的轨迹数据:

# rosbag play -l ./docs/demo_guide/demo.bag

[ INFO] [1502809442.462789096]: Opening ./docs/demo_guide/demo.bag

Waiting 0.2 seconds after advertising topics... done.

Hit space to toggle paused, or 's' to step.
 [RUNNING]  Bag Time: 1497125289.756657   Duration: 20.614178 / 41.613536
 [RUNNING]  Bag Time: 1497125289.896669   Duration: 20.754189 / 41.613536
... ...

我们打开hmi页面上的Debug开关,点击右上角的”Dreamview”按钮,稍后片刻,你就会在新打开的页面上看到小车仿真寻迹行驶的场景了:

img{512x368}

最初实验时,由于没有在阿里云的防火墙打开8888端口,导致dreamview的websocket建立连接失败,dreamview页面始终无法显示出小车。后经与apollo team的ycool在线联调才发现这个问题。这个问题的解决方法也已更新到Apollo的FAQ中了。

四、小结

Baidu为apollo项目做了一个4年的规划(见下面的roadmap),并计划在2020年实现全路网自动驾驶,这个说法似乎有意避开了自动驾驶的级别,这个2020目标到底是L4呢还是L5呢?不过无论是L4还是L5,这个目标都十分有挑战啊。

img{512x368}

个人觉得:未来的L4、L5级别的自动驾驶一定不光光是依靠车辆自身的设备与算法,还要与道路基础设施相配合去实现。甚至是依赖车与车之间的通信才能做到全天候、全路况的自动驾驶。apollo虽然迈出了第一步,但任重道远,让我们拭目以待吧!


微博:@tonybai_cn
微信公众号:iamtonybai
github.com: https://github.com/bigwhite

Hello, ROS

ROS,全称是Robot Operating System,字面译为“机器人操作系统”。不过ROS并非是一个真正意义上的操作系统,而仅仅是一套用于机器人操作和控制软件开发的开发框架(framework),包括各种库和工具。

ROS在2007年诞生于斯坦福大学人工智能实验室Stanford Artificial Intelligence Laboratory,简称SAIL;2008年至2013年,ROS的开发和推广由Willow Garage公司(该公司2014年已关门大吉)主导。2013年8月,ROS的管理权转移给了Open Source Robotics Foundation。截至目前,ROS已经成为全世界使用和支持最为广泛的机器人开发框架之一。

一、ROS简介

ROS推出的初衷旨在降低机器人类软件开发的门槛,提高复用率,避免机器人软件开发人员的重复劳动,快速搭建机器人原型。因此,它采用了当时流行的面向服务SOA的软件架构,最大程度上降低内部耦合,并且易于被第三方扩展。采用C++作为主要开发语言,提高ROS的可移植性,让ROS可以很方便地移植到其他各种CPU体系和OS上。

ROS最初的是针对单机家用移动智能机器人而设计的,因此ROS1版本在以下几方面尚存不足:

  • 鲁棒性

ROS1版本运行时仅有一个master node,一旦master node发生crash,整个robot将无法正常工作。

  • 安全性

ROS1内部完全不设防,Node间通信完全是信赖的。任何Node都可以轻易得到其他node的各种topic数据、参数以及访问相关关键service。

  • 实时性

在ROS1的设计约束下,ROS内部各个节点间产生的实时数据通过master建立的内部网络在各个node间传递。一旦数据量很大,数据可能因内部网络通信性能问题而导致延迟,致使机器人工作异常。这也是ROS在工业机器人领域并未受到“热烈欢迎”的重要原因之一。

为了解决上述这些问题,ROS启动了ROS2的设计和实现。ROS2的第一个alpha版本发布于2015年,最新一个版本是今年七月份发布的beta2版本,ROS2的1.0版本计划将于今年年末正式发布。不过对于ROS2,笔者了解也不多,感兴趣的童鞋可以移步其wiki观看详情。

二、ROS1安装

在深入ROS1之前,我们先来安装一个ROS1。我们首先需要选择一个ROS1的发布版。ROS的发布模式与Ubuntu极其相似:每逢偶数年份发布一个长期支持版(LTS),support 5年;每逢奇数年份发布一个支持2年的版本。

并且ROS的发布版与Ubuntu发布版有着“神同步”:

2014:     ROS Indigo Igloo  对应  Ubuntu 14.04 LTS
2016:     ROS Kinetic Kame 对应 Ubuntu 16.04 LTS

ROS主要基于Ubuntu这款OS进行开发和测试,所以官方建议ROS尽量与Ubuntu一并使用,当然其他linux distribution也可以安装ROS,但正确性和稳定性ROS不能给予明确的保证。目前ROS1发布版的最新版本为:ROS Lunar Loggerhead,但官方推荐使用Ubuntu 16.04 + ROS Kinetic Kame组合;不过由于KK版本发布也就一年出头,市面上更多组织采用的可能还是Ubuntu 14.04 + ROS Indigo Igloo组合。

这里以Ubuntu 16.04+ ROS Kinetic Kame简单说明一下ROS1的安装过程:

1、获取source list并update源

# sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'
# apt-key adv --keyserver hkp://ha.pool.sks-keyservers.net:80 --recv-key 421C365BD9FF1F717815A3895523BAEEB01FA116
Executing: /tmp/tmp.gJDpQgL6qG/gpg.1.sh --keyserver
hkp://ha.pool.sks-keyservers.net:80
--recv-key
421C365BD9FF1F717815A3895523BAEEB01FA116
gpg: requesting key B01FA116 from hkp server ha.pool.sks-keyservers.net
gpg: key B01FA116: public key "ROS Builder <rosbuild@ros.org>" imported
gpg: Total number processed: 1
gpg:               imported: 1

如果需要代理,可以用:
apt-key adv --keyserver-options http-proxy=<myProxy>  --keyserver hkp://ha.pool.sks-keyservers.net:80 --recv-key 421C365BD9FF1F717815A3895523BAEEB01FA116

# apt-get update

2、安装kk版本

ROS有几个release配置供你选择安装:ROS-Base、Desktop Install和Desktop-Full Install,Desktop-Full Install是官方推荐的选项,也是安装最全的选项,它包含了ROS, rqt, rviz, robot-generic libraries, 2D/3D simulators, navigation and 2D/3D perception等package:

# apt-get install ros-kinetic-desktop-full

这个过程需要好长一段时间(依你的网络情况而定),因为ROS超级庞大,有大约2G的安装文件要下载安装。

3、初始化ROS依赖

在使用ROS之前,我们还得先初始化ROS的一些依赖,ROS为你提供了“一键式”的初始化命令:

# rosdep init
Wrote /etc/ros/rosdep/sources.list.d/20-default.list
Recommended: please run

    rosdep update

# rosdep update
reading in sources list data from /etc/ros/rosdep/sources.list.d
Hit https://raw.githubusercontent.com/ros/rosdistro/master/rosdep/osx-homebrew.yaml
Hit https://raw.githubusercontent.com/ros/rosdistro/master/rosdep/base.yaml
... ...
Hit https://raw.githubusercontent.com/ros/rosdistro/master/rosdep/python.yaml
Hit https://raw.githubusercontent.com/ros/rosdistro/master/rosdep/ruby.yaml
Hit https://raw.githubusercontent.com/ros/rosdistro/master/releases/fuerte.yaml
Query rosdistro index https://raw.githubusercontent.com/ros/rosdistro/master/index.yaml
Add distro "groovy"
Add distro "hydro"
Add distro "indigo"
Add distro "jade"
Add distro "kinetic"
Add distro "lunar"
updated cache in /home/tonybai/.ros/rosdep/sources.cache
... ...

到这里,我们可以看到ROS被安装到/opt/ros/kinetic下面了:

# tree -L 1  /opt/ros/kinetic
/opt/ros/kinetic
├── bin
├── env.sh
├── etc
├── include
├── lib
├── setup.bash
├── setup.sh
├── _setup_util.py
├── setup.zsh
└── share

5 directories, 5 files

4、设置环境变量

ROS提供了设置环境变量的脚本:/opt/ros/kinetic/setup.bash,我们将其加入到.bashrc中,这样每次用户登录后就可以使用下面这些ROS专属环境变量了:

# echo "source /opt/ros/kinetic/setup.bash" >> ~/.bashrc
# source ~/.bashrc

# env|grep ROS
ROS_ROOT=/opt/ros/kinetic/share/ros
ROS_PACKAGE_PATH=/opt/ros/kinetic/share
ROS_MASTER_URI=http://localhost:11311
ROSLISP_PACKAGE_DIRECTORIES=
ROS_DISTRO=kinetic
ROS_ETC_DIR=/opt/ros/kinetic/etc/ros

5、安装一些用于ROS package构建的工具依赖

ROS的用户会创建自己的ROS package,为了方便构建这些user package,我们需要安装以下一些工具:

# apt-get install python-rosinstall python-rosinstall-generator python-wstool build-essential

6、验证安装结果

完成以上操作后,ROS kk版本就安装OK了,我们来验证一下安装结果是否正确。我们来尝试启动一下ROS的master node:

# roscore
... logging to /root/.ros/log/fc6a002e-75cf-11e7-b053-00163e1001d7/roslaunch-myhost-7609.log
Checking log directory for disk usage. This may take awhile.
Press Ctrl-C to interrupt
Done checking log file disk usage. Usage is <1GB.

started roslaunch server http://myhost:43606/
ros_comm version 1.12.7

SUMMARY
========

PARAMETERS
 * /rosdistro: kinetic
 * /rosversion: 1.12.7

NODES

auto-starting new master
process[master]: started with pid [7620]
ROS_MASTER_URI=http://myhost:11311/

setting /run_id to fc6a002e-75cf-11e7-b053-00163e1001d7
process[rosout-1]: started with pid [7633]
started core service [/rosout]

如果你看到上面这些roscore的输出,那么基本就证明你的ROS1安装成功了!

三、ROS架构

ROS安装完毕后,我们来对ROS做进一步的探索!先来看看ROS1的架构。

ROS文档中将ROS架构分为三个级别:Filesystem level、Computation Graph level和Community level。对于一个framework来说,从字面意义上理解这三个level还是有些晦涩的。Community level先不说,我们可以通过对照来理解Filesystem level和Computation Graph level,实质上它们一个对应的是ROS的静态结构,一个对应的则是ROS的运行时结构。

1、ROS Filesystem level

我们这里借用《Effective Robotics Programming with ROS 3rd》中的图来整体看一下ROS Filesystem的概念:

img{512x368}

ROS实质上是由一系列的packages组成的,在packages的基础上,ROS通过metapackage来聚合一组packages以形成一个逻辑package。基于metapackage和package概念,ROS为开发者提供了在package之间跳转、文件拷贝、包查找、执行等功能的”类FileSystem”命令集合,比如:roscd、rosls、roscp、rosrun、roscat、rospack等。下面是一些命令使用的例子:

// 切换到ros安装目录
root@myhost:~# roscd
root@myhost:/opt/ros/kinetic#

// 切换到turtlesim包目录
root@myhost:~# roscd turtlesim
root@myhost:/opt/ros/kinetic/share/turtlesim#

// list turtlesim包内的文件
root@myhost:~# rosls turtlesim
cmake  images  msg  package.xml  srv

// 查找turtlesim包的路径
root@myhost:~# rospack find turtlesim
/opt/ros/kinetic/share/turtlesim

// 执行包turtlesim下的turtlesim_node
root@myhost:~# rosrun turtlesim turtlesim_node

// 查看包turtlesim的package.xml内容
root@myhost:~# roscat turtlesim package.xml

<?xml version="1.0"?>
<package>
  <name>turtlesim</name>
  <version>0.7.1</version>
  <description>
    turtlesim is a tool made for teaching ROS and ROS packages.
  </description>
... ...
</package>

ROS安装后,其所有package均存储在$ROS_PACKAGE_PATH下面,初始情况下即为/opt/ros/kinetic/share:

root@myhost:/opt/ros/kinetic# ls share
actionlib                         eigen_stl_containers          laser_pipeline         rosbag_migration_rule  roswtf                 shape_msgs
actionlib_msgs                    executive_smach               librviz_tutorial       rosbag_storage         rqt_action             simulators
actionlib_tutorials               filters                       map_msgs               ros_base               rqt_bag                smach
... ...

每个package下的结构都类似,以turtlesim包为例:

root@myhost:/opt/ros/kinetic/share# ls -F turtlesim
cmake/  images/  msg/  package.xml  srv/

至此,上面图片中package中的结构似乎与上面看到的turtlesim package中的结构对应上了。每个package下面都至少有一个package.xml作为package的manifests,msg、srv是功能性配置,分别定义了package用到的message和提供的service的结构。这里并没有代码,只是一些配置信息。

而对应的包的可执行文件则在/opt/ros/kinetic/lib下,还是以turtlesim package为例,当我们执行下面命令时:

# rosrun turtlesim turtlesim_node

rosrun首先会到$ROS_PACKAGE_PATH下找是否有package.xml中name为”turtlesim”的package(与目录的名字无关)。如果有,rosrun会到/opt/ros/kinetic/lib/turtlesim下查找是否有turtlesim_node这个二进制可执行文件。存在,则启动之;否则报错。

root@myhost:/opt/ros/kinetic/lib/turtlesim# ls
draw_square  mimic  turtlesim_node  turtle_teleop_key

root@myhost:/opt/ros/kinetic/lib/turtlesim# rosrun turtlesim turtlesim_node
[ INFO] [1501549501.410816841]: Starting turtlesim with node name /turtlesim
[ INFO] [1501549501.428589492]: Spawning turtle [turtle1] at x=[5.544445], y=[5.544445], theta=[0.000000]

还有一种package:metapackage。metapackage在目录结构上与普通package无异,但package.xml尾部多了metapackage标签,我们以ros_core/package.xml为例:

<package>
  <name>ros_core</name>
  <version>1.3.1</version>

  <buildtool_depend>catkin</buildtool_depend>

  <run_depend>catkin</run_depend>
  <run_depend>cmake_modules</run_depend>
  <run_depend>common_msgs</run_depend>
  <run_depend>gencpp</run_depend>
  <run_depend>geneus</run_depend>
  <run_depend>genlisp</run_depend>
  <run_depend>genmsg</run_depend>
  <run_depend>gennodejs</run_depend>
  <run_depend>genpy</run_depend>
  <run_depend>message_generation</run_depend>
  <run_depend>message_runtime</run_depend>
  <run_depend>ros</run_depend>
  <run_depend>ros_comm</run_depend>
  <run_depend>rosbag_migration_rule</run_depend>
  <run_depend>rosconsole_bridge</run_depend>
  <run_depend>roscpp_core</run_depend>
  <run_depend>rosgraph_msgs</run_depend>
  <run_depend>roslisp</run_depend>
  <run_depend>rospack</run_depend>
  <run_depend>std_msgs</run_depend>
  <run_depend>std_srvs</run_depend>

  <export>
    <metapackage/>
  </export>
</package>

这种包称为metapackage,它的实质是一组package的集合。

2、ROS Computation Graph level

说完了ROS的静态结构,我们再来看看ROS整体的运行时结构,即ROS Computation Graph level:

img{512x368}

ROS在运行时层面是由一个master和一组node组成的,master的作用就是名字注册和查找,建立node与topic间联系以及服务发现之用。node间的通信方式可以是:

  • 服务srv调用
  • topic的发布和订阅

我们通过rosnode命令可以操作node,比如查看当前ROS中node信息:

# rosnode list
/rosout
/turtlesim

/rosout node是一个由roscore命令启动的特殊node,它相当于整个ROS运行环境的stdout/stderr,将所有node发往/rosout topic的消息汇聚在一起。

每个ROS运行时环境有且仅有一个ros master,ros master通过执行roscore命令启动,这也是一个ROS运行环境启动最先应该执行的命令:

# roscore
... logging to /home/tonybai/.ros/log/ee13b88e-7666-11e7-af90-4ccc6a7061a6/roslaunch-tonybai-myhost-26158.log
Checking log directory for disk usage. This may take awhile.
Press Ctrl-C to interrupt
Done checking log file disk usage. Usage is <1GB.

started roslaunch server http://tonybai-myhost:36180/
ros_comm version 1.12.7

SUMMARY
========

PARAMETERS
 * /rosdistro: kinetic
 * /rosversion: 1.12.7

NODES

auto-starting new master
process[master]: started with pid [26169]
ROS_MASTER_URI=http://tonybai-myhost:11311/

setting /run_id to ee13b88e-7666-11e7-af90-4ccc6a7061a6
process[rosout-1]: started with pid [26182]
started core service [/rosout]

roscore位于/opt/ros/kinetic/bin下,它实际上是一个python脚本,它调用位于/opt/ros/kinetic/lib/python2.7/dist-packages/roslaunch下的roslaunch lib,并依据launch配置文件/opt/ros/kinetic/etc/ros/roscore.xml启动对应的核心node:

// /opt/ros/kinetic/etc/ros/roscore.xml
<!--
  ROS Core Stack definition

  Before making any modifications to this file, please read:

http://ros.org/wiki/roscore

  -->
<launch>
  <group ns="/">
    <param name="rosversion" command="rosversion roslaunch" />
    <param name="rosdistro" command="rosversion -d" />
    <node pkg="rosout" type="rosout" name="rosout" respawn="true"/>
  </group>
</launch>

roscore会自动启动master,master对应的是一个metapackage: ros。ros package的package.xml如下:

<package>
  <name>ros</name>
  <version>1.13.5</version>
  <description>ROS packaging system</description>
  <maintainer email="dthomas@osrfoundation.org">Dirk Thomas</maintainer>
  ... ...

  <buildtool_depend>catkin</buildtool_depend>

  <run_depend>catkin</run_depend> <!-- only for backward compatibility with rosbuild -->
  <run_depend>mk</run_depend>
  <run_depend>rosbuild</run_depend>
  <run_depend>roslang</run_depend>
  <run_depend>roslib</run_depend>
  <run_depend>rosbash</run_depend>
  <run_depend>rosboost_cfg</run_depend>
  <run_depend>rosclean</run_depend>
  <run_depend>roscreate</run_depend>
  <run_depend>rosmake</run_depend>
  <run_depend>rosunit</run_depend>

  <export>
    <metapackage/>
  </export>
</package>

ROS的运行时当前目录为~/.ros,在这个目录下你会看到ros的一些运行时输出信息:

$ tree  -L 1 ~/.ros
/home/tonybai/.ros
├── log/
├── roscore-11311.pid
├── rosdep/
├── rospack_cache_00988404638878154258
├── rospack_cache_04359245844500407984
├── rospack_cache_05251971726343818934
├── rospack_cache_11134725904490598093
├── rosstack_cache_00988404638878154258
├── rosstack_cache_04359245844500407984
├── rosstack_cache_05251971726343818934
└── rosstack_cache_11134725904490598093

2 directories, 9 files

roscore还会启动一个Parameter Server,用于各个节点保存或读取parameters,通过rosparam可以查看相关param信息,比如当前param的list:

$ rosparam list
/background_b
/background_g
/background_r
/rosdistro
/roslaunch/uris/host_tonybai_myhost__36180
/rosversion
/run_id

我们可以通过ros提供的rqt_graph命令查看node之间以及node与topic之间的订阅和发布关系,如下图:

img{512x368}

3、ROS的“分布式”源码结构

安装过程中ROS的“庞大”,与ROS在github上源码库的“渺小”形成鲜明对比。其实我们安装的ROS与这份源码库并非一一对应的:ROS的源码结构也是“分布式”的,ROS源码实质上是一系列package源码的组合。当前版本的ROS发布版采用bloom工具进行release的。以kk版本为例,bloom读取一份rosdistro库的kk版本distribution.yaml文件(这份文件比较庞大),即ROS发布文件,并根据文件中的描述信息,下载对应的package源码并编译构建的:

// kinetic/distribution.yaml)
%YAML 1.1
# ROS distribution file
# see REP 143: http://ros.org/reps/rep-0143.html
---
release_platforms:
  debian:
  - jessie
  fedora:
  - '23'
  - '24'
  ubuntu:
  - xenial
repositories:
  abb:
    doc:
      type: git
      url: https://github.com/ros-industrial/abb.git
      version: kinetic-devel
    release:
      packages:
      - abb
      - abb_driver
      - abb_resources
      ... ...
      tags:
        release: release/kinetic/{package}/{version}
      url: https://github.com/ros-industrial-release/abb-release.git
      version: 1.3.0-1
    source:
      type: git
      url: https://github.com/ros-industrial/abb.git
      version: kinetic
    status: developed
  abb_experimental:
    doc:
      type: git
      url: https://github.com/ros-industrial/abb_experimental.git
      version: kinetic-devel
    status: developed
... ...
type: distribution
version: 2

鉴于ROS这种分布式的相对松散的源码组织结构,对ROS的裁剪则相对简单,只需挑选你自己需要的第三方包即可。

四、启动你的第一个ROS“机器人”

ROS虽然号称机器人开发框架,但拥有一个实体版机器人并不是进行ROS开发的必要条件。ROS的一大优势就是可以利用各种仿真工具进行机器人操作和控制逻辑的仿真和调试。常见的仿真器主要有三个:TurtlesimRviz+arbotixGazebo。Turtlesim是一个QT开发的2D轨迹显示界面,只能显示运动轨迹;arbotix是含有一个差速驱动机器人的控制器,结合rviz使用,用于机器人运动及topic数据的3D显示,但不包含物理学引擎;Gazebo是全功能的3D物理模拟器,要用这个模拟器,需要掂量掂量你的主机的内存和显卡是否够用。

本文是入门文章,我们就从turtlesim开始。假设此时roscore已经启动了。

我们来启动一下turtlesim_node:

# rosrun turtlesim turtlesim_node
[ INFO] [1501549501.410816841]: Starting turtlesim with node name /turtlesim
[ INFO] [1501549501.428589492]: Spawning turtle [turtle1] at x=[5.544445], y=[5.544445], theta=[0.000000]

这时你的desktop会出现一个新的窗口,如下图:

img{512x368}

不过此时小海龟一动不动!如果要让它移动,我们需要告诉他如何移动!

我们启动另外一个node – turtle_teleop_key:

# rosrun turtlesim turtle_teleop_key
Reading from keyboard
---------------------------
Use arrow keys to move the turtle.

通过turtle_teleop_key,我们可以使用方向键控制小海龟的移动了:

img{512x368}

其原理在于:turtle_teleop_key将方向键产生的数据转换为位置信息后,发布到topic: /turtle1/cmd_vel上;turtlesim_node由于subscribe了该topic,因此将接收到新的位置数据,这样小海龟就会移动到新的位置上去:

img{512x368}

turtlesim node启动后还启动了一个service: spawn,调用该服务我们可以在窗口上创建出一个新的小海龟:

# rosservice call /spawn 2 2 0.2 ""
name: turtle2

img{512x368}

可以看到,通过service调用或向topic发布数据,我们可以自由控制小海龟。下面的是一个稍微复杂的控制指令,其结果就是让小海龟1进行持续的转圈动作:

rostopic pub /turtle1/cmd_vel geometry_msgs/Twist -r 1 -- '[2.0, 0.0, 0.0]' '[0.0, 0.0, -1.8]'

img{512x368}

五、创建你的第一个ROS package

现在我们来创建第一个ROS package!

1、初始化ros workspace

我们要添加自己的ROS package,一般不会直接在ROS的安装目录下创建,因此我们需要创建自己的workspace,并在后续将其加入到ROS_PACKAGE_PATH中,以使得ros的文件系统命令也能适用于我们自己的workspace路径。

# mkdir -p ~/myros_ws/src
# cd ~/myros_ws/src
# catkin_init_workspace
Creating symlink "/home/tonybai/myros_ws/src/CMakeLists.txt" pointing to "/opt/ros/kinetic/share/catkin/cmake/toplevel.cmake"

$ tree ~/myros_ws/
/home/tonybai/myros_ws/
└── src
    └── CMakeLists.txt -> /opt/ros/kinetic/share/catkin/cmake/toplevel.cmake

1 directory, 1 file

2、创建Package

我们来创建一个我们自己的package – chattingsim:

# cd ~/myros_ws/src
# catkin_create_pkg chattingsim std_msgs rospy roscpp
Created file chattingsim/package.xml
Created file chattingsim/CMakeLists.txt
Created folder chattingsim/include/chattingsim
Created folder chattingsim/src
Successfully created files in /home/tonybai/myros_ws/src/chattingsim. Please adjust the values in package.xml.

# tree chattingsim/
chattingsim/
├── CMakeLists.txt
├── include
│   └── chattingsim
├── package.xml
└── src

3 directories, 2 files

虽然目前我们的chattingsim package并没有任何有意义的代码,但不妨碍我们先来编译一下myros_ws这个workspace:

# cd ~/myros_ws/
# catkin_make

# catkin_make
Base path: /home/tonybai/myros_ws
Source space: /home/tonybai/myros_ws/src
Build space: /home/tonybai/myros_ws/build
Devel space: /home/tonybai/myros_ws/devel
Install space: /home/tonybai/myros_ws/install
####
#### Running command: "cmake /home/tonybai/myros_ws/src -DCATKIN_DEVEL_PREFIX=/home/tonybai/myros_ws/devel -DCMAKE_INSTALL_PREFIX=/home/tonybai/myros_ws/install -G Unix Makefiles" in "/home/tonybai/myros_ws/build"
####
-- The C compiler identification is GNU 5.4.0
-- The CXX compiler identification is GNU 5.4.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
... ...
-- Looking for pthread_create in pthread
-- Looking for pthread_create in pthread - found
-- Found Threads: TRUE
-- Found gtest sources under '/usr/src/gtest': gtests will be built
-- Using Python nosetests: /usr/bin/nosetests-2.7
-- catkin 0.7.6
-- BUILD_SHARED_LIBS is on
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- ~~  traversing 1 packages in topological order:
-- ~~  - chattingsim
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- +++ processing catkin package: 'chattingsim'
-- ==> add_subdirectory(chattingsim)
-- Configuring done
-- Generating done
-- Build files have been written to: /home/tonybai/myros_ws/build
####
#### Running command: "make -j4 -l4" in "/home/tonybai/myros_ws/build"
####

catkin_make后,myros_ws下面又增加了不少目录和文件:

~/myros_ws$ tree -L 2
.
├── build
│   ├── catkin
│   ├── catkin_generated
│   ├── CATKIN_IGNORE
│   ├── catkin_make.cache
│   ├── chattingsim
│   ├── CMakeCache.txt
│   ├── CMakeFiles
│   ├── cmake_install.cmake
│   ├── CTestTestfile.cmake
│   ├── gtest
│   ├── Makefile
│   └── test_results
├── devel
│   ├── env.sh
│   ├── lib
│   ├── setup.bash
│   ├── setup.sh
│   ├── _setup_util.py
│   ├── setup.zsh
│   └── share
└── src
    ├── chattingsim
    └── CMakeLists.txt -> /opt/ros/kinetic/share/catkin/cmake/toplevel.cmake

12 directories, 12 files

我们看到~/myros_ws/devel目录下的结构与/opt/ros/kinetic下的非常相似,我们将其加入到ROS_PACKAGE_PATH:

# cd ~/myros_ws/devel
# source ./setup.bash
# echo $ROS_PACKAGE_PATH
/home/tonybai/myros_ws/src:/opt/ros/kinetic/share

3、添加talker和listener

chattingsim package的架子已经搭好,接下来我们开始“填肉”。这里我们直接使用ros tutorials中写好的两个源文件talker.cpplistener.cpp,我们把这两个文件放在~/myros_ws/src/chattingsim/src下面。

在build之前,我们需要修改一下chattingsim的CMakeLists.txt:

cmake_minimum_required(VERSION 2.8.3)
project(chattingsim)

find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  genmsg
)

generate_messages(DEPENDENCIES std_msgs)

include_directories(
  include ${catkin_INCLUDE_DIRS}
)

add_executable(talker src/talker.cpp)
target_link_libraries(talker ${catkin_LIBRARIES})
add_dependencies(talker chattingsim_generate_messages_cpp)

add_executable(listener src/listener.cpp)
target_link_libraries(listener ${catkin_LIBRARIES})
add_dependencies(listener chattingsim_generate_messages_cpp)

构建chattingsim package:

~/myros_ws# catkin_make
Base path: /home/tonybai/myros_ws
Source space: /home/tonybai/myros_ws/src
Build space: /home/tonybai/myros_ws/build
Devel space: /home/tonybai/myros_ws/devel
Install space: /home/tonybai/myros_ws/install
####
#### Running command: "make cmake_check_build_system" in "/home/tonybai/myros_ws/build"
####
####
#### Running command: "make -j4 -l4" in "/home/tonybai/myros_ws/build"
####
[  0%] Built target std_msgs_generate_messages_eus
[  0%] Built target std_msgs_generate_messages_cpp
[  0%] Built target std_msgs_generate_messages_lisp
[  0%] Built target std_msgs_generate_messages_py
[  0%] Built target std_msgs_generate_messages_nodejs
[  0%] Built target chattingsim_generate_messages_cpp
[  0%] Built target chattingsim_generate_messages_lisp
[ 14%] Generating EusLisp manifest code for chattingsim
[ 28%] Building CXX object chattingsim/CMakeFiles/talker.dir/src/talker.cpp.o
[ 28%] Built target chattingsim_generate_messages_nodejs
[ 42%] Generating Python msg __init__.py for chattingsim
[ 57%] Generating Python srv __init__.py for chattingsim
[ 71%] Building CXX object chattingsim/CMakeFiles/listener.dir/src/listener.cpp.o
[ 71%] Built target chattingsim_generate_messages_py
[ 71%] Built target chattingsim_generate_messages_eus
[ 71%] Built target chattingsim_generate_messages
[ 85%] Linking CXX executable /home/tonybai/myros_ws/devel/lib/chattingsim/talker
[ 85%] Built target talker
[100%] Linking CXX executable /home/tonybai/myros_ws/devel/lib/chattingsim/listener
[100%] Built target listener

4、启动chattingsim的talker node和listener node

在两个terminal窗口分别启动listener node和talker node:

# rosrun chattingsim listener
[ INFO] [1501577165.148477238]: I heard: [hello world 3]
[ INFO] [1501577165.248349227]: I heard: [hello world 4]
[ INFO] [1501577165.348301478]: I heard: [hello world 5]
[ INFO] [1501577165.448340592]: I heard: [hello world 6]
[ INFO] [1501577165.548433696]: I heard: [hello world 7]
[ INFO] [1501577165.648466054]: I heard: [hello world 8]
[ INFO] [1501577165.748424131]: I heard: [hello world 9]
[ INFO] [1501577165.848457076]: I heard: [hello world 10]
[ INFO] [1501577165.948449431]: I heard: [hello world 11]
[ INFO] [1501577166.048470110]: I heard: [hello world 12]
[ INFO] [1501577166.148340964]: I heard: [hello world 13]

# rosrun chattingsim talker
[ INFO] [1501577164.847745179]: hello world 0
[ INFO] [1501577164.947898377]: hello world 1
[ INFO] [1501577165.047889213]: hello world 2
[ INFO] [1501577165.147882701]: hello world 3
[ INFO] [1501577165.247923700]: hello world 4
[ INFO] [1501577165.347918242]: hello world 5
[ INFO] [1501577165.447917169]: hello world 6
[ INFO] [1501577165.547916593]: hello world 7
[ INFO] [1501577165.647920474]: hello world 8
[ INFO] [1501577165.747930882]: hello world 9
[ INFO] [1501577165.847917356]: hello world 10
[ INFO] [1501577165.947918365]: hello world 11
[ INFO] [1501577166.047918187]: hello world 12
[ INFO] [1501577166.147919712]: hello world 13
^C[ INFO] [1501577166.247984284]: hello world 14

至此,基于我们第一个package: chattingsim而创建的node工作正常!

六、小结

如果说人工智能的算法是大脑,实体的机械部件构成四肢,那么ROS则提供了大脑与各肢体间提供了神经传递的机制。之前ROS在国内发展的不瘟不火,随着Baidu Apollo项目将ros作为Apollo-platform支持的一部分,更多人会去了解ROS,ROS在国内的发展势也许会走得更顺畅一些。ROSCon 2017也即将于下月在加拿大温哥华召开,ROS2对ROS1的安全性和实时性的加强也势必会让ROS有更多用武之地,值得期待。

注:ros wiki 资料非常详尽,ros tutorial是学习ros的起点,几乎不用任何其他书籍。


微博:@tonybai_cn
微信公众号:iamtonybai
github.com: https://github.com/bigwhite




这里是Tony Bai的个人Blog,欢迎访问、订阅和留言!订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:


以太币:


如果您喜欢通过微信App浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:



本站Powered by Digital Ocean VPS。

选择Digital Ocean VPS主机,即可获得10美元现金充值,可免费使用两个月哟!

著名主机提供商Linode 10$优惠码:linode10,在这里注册即可免费获得。

阿里云推荐码:1WFZ0V立享9折!

View Tony Bai's profile on LinkedIn


文章

评论

  • 正在加载...

分类

标签

归档











更多