精华内容
下载资源
问答
  • GB28181平台,监控接入从理论到实现

    千次阅读 2021-01-25 18:40:22
    尽管在国标GB28181中并没有对“平台”进行明确的定义,但在规范中却多次提到“系统平台”、“管理平台”等词汇,在具体项目中、网络上的交流学习中,平台概念也是无处不在。笔者认为,GB28181平台就是视频联网系统中...

    一、GB28181规范

    尽管在国标GB28181中并没有对“平台”进行明确的定义,但在规范中却多次提到“系统平台”、“管理平台”等词汇,在具体项目中、网络上的交流学习中,平台概念也是无处不在。笔者认为,GB28181平台就是视频联网系统中的上级平台、中间平台或下级平台,用于实现信令、音视频数据的接收和转发。因此,在一个GB28181平台中至少应包含一个SIP信令服务器和一个流媒体服务器,这两个(类)服务器和摄像头、播放终端等设备一起组成一个监控域。对于绝大多数项目,平台中还应包括针对具体业务的业务平台,如指挥控制中心等,我这里只重点将信令服务器和流媒体服务器。

    GB28181平台.png
    平台示意图

     

     

    SIP信令服务器

    SIP信令服务器负责对平台中的控制指令进行接收和转发,具有向SIP客户端、SIP设备、媒体服务器和网关等提供注册、路由选择以及逻辑控制等功能。具体指令包括注册、实时视频播放、历史视频播放、设备控制、信息查询、报警事件通知等。具体的实现遵循SIP协议规范,会话控制采用IETFRFC3261规定的Register、Invite等请求和响应方法实现,历史视音频回放控制采用SIP扩展协议IETFRFC2976规定的INFO 方法实现,前端设备控制、信息查询、报警事件通知和分发等应用的会话控制采用SIP扩展协议IETFRFC3428规定的Message方法实现。SIP消息的传输支持采用UDP和TCP两种传输协议。

    无论采用UDP还是TCP协议,在信令交互流程上是一致的,我们以注册和播放实时视频为例,实现流程如下:

    注册.png
    注册流程

     

    实时视频播放流程
    实时视频播放流程

     

    流媒体服务器

    流媒体服务器的作用是接收和转发音视频流,包括接收监控设备发送携带音视频的数据包,向播放终端或其他平台发送音视频包等。视音频传输协议要求采用RTP协议,视频推荐使用H264编码,音频G711/G723。传输层协议同样最好同时支持UDP协议和TCP协议,UDP协议带来更好的实时性和更低的延迟,TCP协议则提供更可靠的传输。

    尽管标准对视频的上行和下行(播放)都明确定义要RTP协议,但是在实际应用中存在向各种终端(手机、浏览器)、各种业务平台转发音视频的需求,因此流媒体服务器的实现应当提供更多的灵活性,在采用RTP协议接收和转发音视频的同时,应当支持更多播放协议,以支持更广泛的终端集成播放需求。一些流媒体服务器平台,如云视睿博的NTV Media Server G3和SRS项目,都在支持RTP协议的同时提供了更多的播出协议支持。

    28181.jpg
    NTV Media Server G3联网示意图

     

    二、平台实现和配置

    有了上述的基础规范介绍,在做具体设备和平台配置的时候就相对明确多了,在摄像头或DVR中,应当平台的IP地址、域和平台编码等信息。而在平台端,则可以提前注册设备编号,已提供更安全的设备接入管理(平台提前注册设备编号是可选的,规范并没有约定)。

    平台配置

    平台端的配置差别稍大,我们以NTV Media Server为例,配置流程如下:

    进入NTV Media Server G3后台管理界面,点击菜单项“gb28181设备”:

    server1.png


    然后在设备管理界面点击“添加”按钮:

    server2.png

    添加摄像头的操作比较简单,只要输入摄像头名称,点击“确定”按钮即可:

    server3.png

    这样,一个摄像头的配置就做好了,在摄像头管理界面中,将生成的配置信息配置到摄像头中,参见下一节的介绍。

    终端配置

    摄像头的配置界面,不同品牌的配置界面稍有差别,但需要配置的项目基本一致,我们以海康威视的摄像头为例进行配置。

    以海康威视的摄像头为例,登录摄像头的Web管理界面,如下图:

    camaral1.png

    具体配置流程:

    点击左侧测单“网络”-->“高级配置”,显示界面如上图。点击“平台接入”按钮,出现配置界面,平台接入方式选择“28181”,会出现和上图一致的配置界面。

    将上一小节中流媒体服务器上生成的摄像头配置信息配置到摄像头中,注意要逐项比对配置,避免输入错误。记着点开最下面“视频编码通道ID”配置框,将正确的视频通道编码填入进去,否者无法正常发送视频。

    配置完毕后,点击“保存”按钮。

    这样就完成了摄像头和流媒体服务器的对接配置,如果摄像头的网络没有问题,摄像头就会注册到流媒体服务器上。

     

    展开全文
  • 什么是低代码平台 low-code?

    千次阅读 2021-02-02 09:32:01
    简介:什么是低代码?我们为什么需要低代码?低代码会让程序员失业吗?本文总结了低代码领域的基本概念、核心价值与行业现状,带你全面了解低代码。 一 前言 如果选择用一个关键词来代表即将过去的2020年,我相信...

    简介:什么是低代码?我们为什么需要低代码?低代码会让程序员失业吗?本文总结了低代码领域的基本概念、核心价值与行业现状,带你全面了解低代码。

    一 前言

    如果选择用一个关键词来代表即将过去的2020年,我相信所有人都会认同是“新冠”。疫情来得太快就像龙卷风,短短数月就阻断了全世界范围内无数人与人之间的物理连接。但好在,我们已经全面迈入互联网时代:N95口罩再厚,也阻挡不了信息比特流的顺畅流通(宅男:B站依然香);居家隔离再久,也妨碍不了钉钉消息的准时送达(社畜:工作依然苦)。逍遥子在9月份的云栖大会上说:“新技术代表的新生产力,一定是我们全速战胜疫情、开创未来最好的原动力。” 那么在后疫情时代,究竟需要什么样的新技术,才能真正解放IT生产力,加速社会数字化转型,Make The World Great Again?我认为是低代码(Low-Code)。

    基于经典的可视化和模型驱动理念,结合最新的云原生与多端体验技术,低代码能够在合适的业务场景下实现大幅度的提效降本,为专业开发者提供了一种全新的高生产力开发范式(Paradigm Shift)。另一方面,低代码还能让不懂代码的业务人员成为所谓的平民开发者(Citizen Developer),弥补日益扩大的专业人才缺口,同时促成业务与技术深度协作的终极敏捷形态(BizDevOps)。本文将重点介绍低代码相关背景知识,包括低代码的定义与意义、相关概念、行业发展等,期望能帮助大家更好地认识与理解低代码这个新兴领域。

    二 什么是低代码

    “Low-Code”是什么?如果你是第一次听说,没准也会跟我当年从老板口中听到这个词后的内心戏一样:啥?“Low-Code”?“Code”是指代码我知道,但这个“Low”字是啥意思?不会是老板发现我最近赶工写的代码很丑很“Low”吧... 想多了,老板怎么可能亲自review代码呢。那难道是指,“Low-level programming”里的“Low”?老板终于发现让我等编程奇才整天堆Java业务代码太浪费,要派我去闭关写一个高性能C语言网络库... 显然也不是,老板哪能有这技术情怀呢。那到底是什么意思?作为一名搜商比情商还高的程序员,能问Google的绝不会问老板。于是我一顿操作后,不假思索地点开了第一条搜索结果。果不其然,这是一条充满自由芳香只有翻墙才能闻到的Wikipedia词条:Low-code development platform。

    Wikipedia定义

     

    从Wiki的这段定义中,我们可以提炼出几个关键信息:

    • 低代码开发平台(LCDP)本身也是一种软件,它为开发者提供了一个创建应用软件的开发环境。看到“开发环境”几个字是不是很亲切?对于程序员而言,低代码开发平台的性质与IDEA、VS等代码IDE(集成开发环境)几乎一样,都是服务于开发者的生产力工具。
    • 与传统代码IDE不同的是,低代码开发平台提供的是更高维和易用的可视化IDE。大多数情况下,开发者并不需要使用传统的手写代码方式进行编程,而是可以通过图形化拖拽、参数配置等更高效的方式完成开发工作。

    Forrester定义

    顺着Wiki的描述还能发现,原来“Low-Code”一词早在2014年就由Forrester提出了,它对低代码开发平台的始祖级定义是这样的:

     

    相比Wiki的版本,这个定义更偏向于阐明低代码所带来的核心价值:

    • 低代码开发平台能够实现业务应用的快速交付。也就是说,不只是像传统开发平台一样“能”开发应用而已,低代码开发平台的重点是开发应用更“快”。更重要的是,这个快的程度是颠覆性的:根据Forrester在2016年的调研,大部分公司反馈低代码平台帮助他们把开发效率提升了5-10倍。而且我们有理由相信,随着低代码技术、产品和行业的不断成熟,这个提升倍数还能继续上涨。
    • 低代码开发平台能够降低业务应用的开发成本。一方面,低代码开发在软件全生命周期流程上的投入都要更低(代码编写更少、环境设置和部署成本也更简单);另一方面,低代码开发还显著降低了开发人员的使用门槛,非专业开发者经过简单的IT基础培训就能快速上岗,既能充分调动和利用企业现有的各方面人力资源,也能大幅降低对昂贵专业开发者资源的依赖。

    低代码核心能力

    基于上述的定义和分析,不难总结出如下这3条低代码开发平台的核心能力:

     

    • 全栈可视化编程:可视化包含两层含义,一个是编辑时支持的点选、拖拽和配置操作,另一个是编辑完成后所及即所得(WYSIWYG)的预览效果。传统代码IDE也支持部分可视化能力(如早年Visual Studio的MFC/WPF),但低代码更强调的是全栈、端到端的可视化编程,覆盖一个完整应用开发所涉及的各个技术层面(界面/数据/逻辑)。
    • 全生命周期管理:作为一站式的应用开发平台,低代码支持应用的完整生命周期管理,即从设计阶段开始(有些平台还支持更前置的项目与需求管理),历经开发、构建、测试和部署,一直到上线后的各种运维(e.g. 监控报警、应用上下线)和运营(e.g. 数据报表、用户反馈)。
    • 低代码扩展能力:使用低代码开发时,大部分情况下仍离不开代码,因此平台必须能支持在必要时通过少量的代码对应用各层次进行灵活扩展,比如添加自定义组件、修改主题CSS样式、定制逻辑流动作等。一些可能的需求场景包括:UI样式定制、遗留代码复用、专用的加密算法、非标系统集成。

    不只是少写代码

    回到最初那个直击心灵的小白问题:Low-Code中的“Low”,到底是啥意思?答案已经显而易见:既不是指抽象程度很低(相反,低代码开发方式的抽象程度要比传统编程语言高一个level),也不是指代码很low(也相反,低代码所生成的代码一般都经过精心维护和反复测试,整体质量强于大部分手写代码),而是单纯的“少写代码” —— 只在少数需要的情况下才手写代码,其他大部分时候都能用可视化等非代码方式解决。

    再往深一点儿看,低代码不只是少写代码而已:代码写得少,bug也就越少(正所谓“少做少错”),因此开发环节的两大支柱性工作“赶需求”和“修bug”就都少了;要测的代码少了,那么测试用例也可以少写不少;除了开发阶段以外,平台还覆盖了后续的应用构建、部署和管理,因此运维操作也更少了(Low-Code → Low-Ops)。

    然而,少并不是最终目的:如果单纯只是想达到少的效果,砍需求减人力、降低质量要求也是一样的。低代码背后的哲学,是少即是多(Less is More),或者更准确说是多快好省(Do More with Less) —— 能力更多、上线更快、质量更好,成本还更省,深刻践行了阿里“既要,又要,还要”的价值观精髓。

     

    平台的职责与挑战

    上面说的是低代码给开发者提供的能力与吸引力,那么作为服务的提供方与应用的承载者,低代码开发平台自身应该承担怎样的职责,其中又会遇到多大的挑战?是否就一定要如阿里云所主张的那样,“把复杂留给自己,把简单留给别人”?虽然这句话听起来很深明大义,但不知道大家有没有想过,为什么我们一定要抱着复杂不放,平白无故给自己找事?就不能直接干掉复杂,也给咱阿里云自己的员工留点简单吗?是工作太容易就体现不出来KPI价值了,还是家里的饭菜不如公司的夜宵香?

    冥思苦想许久后,我从热力学第一定律中找到了答案:开发一个应用的总复杂度是恒定的,只能转移而不可能凭空消失。要想让开发者做的更少,安心享受简单的快乐,那么平台方就得做的更多,默默承担尽可能多的复杂度。就像一个满身腱子肉的杂技男演员,四平八稳地托举着在高处旋转与跳跃的女搭档;上面的人显得越轻盈越毫不费力,下面的人就得越稳重越用尽全力。当然,不是说上面的女演员就很轻松没压力,只是他们各自的分工不同,所承担的复杂度也不一样。

    根据《人月神话》作者Fred Brooks的划分,软件开发的复杂度可以划分为本质复杂度(Essential complexity )和偶然复杂度(Accidental complexity)。前者是解决问题时固有的最小复杂度,跟你用什么样的工具、经验是否丰富、架构好不好等都无关,而后者就是除此之外在实际开发过程中引入的复杂度。通常来说,本质复杂度与业务要解决的特定问题域强相关,因此这里我把它称为更好理解的“业务复杂度”;这部分复杂度不是任何开发方法或工具能解决的,包括低代码。而偶然复杂度一般与开发阶段的技术细节强相关,因此我也相应把它称为“技术复杂度”;而这一部分复杂度,恰好就是低代码所擅长且适合解决的。

    为开发者尽可能屏蔽底层技术细节、减少不必要的技术复杂度,并支撑其更好地应对业务复杂度(满足灵活通用的业务场景需求),这是身为一个低代码开发平台所应该尽到的核心职责。

     

    在尽到上述职责的同时,低代码开发平台作为一个面向开发者的产品,还需要致力于为开发者提供简单直观的极致开发体验。这背后除了巨大的工作量,还得能在“强大”和“易用”这两个很难两全其美的矛盾点之间,努力找到一个符合自己产品定位与目标客户需求的平衡点 —— 这也许是设计一个通用低代码开发平台所面临的最大挑战。

    三 低代码相关概念对比

    纯代码(Pro-Code / Custom-Code)

    “纯代码”可能算是我杜撰的一个词,更常见的说法是专业代码(Pro-Code)或定制代码(Custom-Code);但意思都一样,就是指传统的以代码为中心(Code-Centric)的开发模式。之所以我选择用“纯代码”,是因为如果用“专业代码”会显得似乎低代码就不专业了一样,而用“定制代码”又容易让人误解成低代码无法支持定制的自定义代码。

    当然,更准确的称谓我认为是“高代码”(与低代码恰好对应,只是名字太难听,被我嫌弃了...),因为即便是使用传统的代码IDE,有些开发工作也支持(甚至更适合)以非代码方式完成,比如:iOS端开发时使用的SwiftUI界面设计器、服务端开发数据库应用时使用的PowerDesigner建模工具。不过这部分可视化工作在传统开发模式下只是起辅助作用,最后通常也是生成开发者可直接修改的代码;开发者仍然是以代码为中心来开展主要工作。

    低代码与纯代码之间的关系,其实跟视频和文章之间很像:

    • 低代码就像是现代的“视频”,大部分内容都由直观易理解、表达能力强的图片组成,因此更容易被大众所接受。但与同时,视频也不是死板得只能有图片,完全可以添加少量文字(如字幕、标注)来弥补图片表达不够精确的问题。BTW,关于“图”和“文字”之间的辩证关系,可以进一步参考《架构制图:工具与方法论》[1]这篇文章中的相关描述。
    • 纯代码则更像是传统的“文章”,虽然很久以来都一直是信息传播的唯一媒介,但自从视频技术诞生以及相应软硬件基础设施的普及以来,便逐渐开始被抢走了风头。如今,视频已成为大部分人获取信息的主要渠道(从电视电影到B站抖音),而经常读书读文章的人却越来越少。但不可否认的是,文章依然有它存在的意义和受众(不然我也不会费这劲敲这么多字了),即使“市场份额”一直在被挤压,但永远会有它立足的空间。

     

    如果按上面这种类比关系推导,低代码未来也会遵循与视频类似的发展轨迹,超越纯代码成为主流开发模式。Gartner的预测也表达了相同的观点:到2024年,所有应用程序开发活动当中的65%将通过低代码的方式完成,同时75%的大型企业将使用至少四种低代码开发工具进行应用开发。

    但同样地,就像是视频永远无法取代文章一样,低代码也永远无法彻底取代纯代码开发方式。未来低代码和纯代码方式将以互补的形态长期共存,各自在其所适合的业务场景中发光发热。在后面的“低代码业务场景”章节,会详细列出哪些场景在现阶段更适合用低代码模式开发。

    零代码(Zero-Code / No-Code)

    从分类的完备性角度来看,有“纯代码”自然也应该有完全相反的“零代码”(也称为“无代码”)。零代码就是完全不需要写代码的应用开发平台,但这并不代表零代码就比低代码更高级和先进,它只是做了一个更极端的选择而已:彻底拥抱简单的图形可视化,完全消灭复杂的文本代码。选择背后的原因是,零代码开发平台期望能尽可能降低应用开发门槛,让人人都能成为开发者(注意:开发 ≠ 写代码),包括完全不懂代码的业务分析师、用户运营,甚至是产品经理(不懂装懂可不算懂)。

    即便是专业开发者,在技术分工越来越精细的趋势下(前端/后端/算法/SRE/数据分析..),也很难招到一个能独立开发和维护整套复杂应用的全栈工程师。但零代码可以改变这一切:无论是Java和JavaScript傻傻分不清楚的技术小白,还是精通深度学习但没时间学习Web开发的算法大牛,都可以通过零代码实现自己的技术梦或全栈梦。“改变世界的idea已有,就差一个程序员了”,这句玩笑话或许真的可以成真;哦不,甚至都用不着程序员,有idea的人自己就能上。

     

    虽然零代码与狭义上的低代码有着上述明显差异,但从广义上来说,零代码可以当作低代码的一个子集。Gartner在其相关调研报告中,就是将“No Code”划在了范围更广的低代码应用平台“LCAP”(Low-Code Application Platform)中。而当前市面上很多通用的低代码开发平台,也都兼具一定程度的零代码能力;比如低代码领域领头羊Mendix,既提供了简单易用的零代码Web IDE - Mendix Studio,也包括一个功能更强大的低代码桌面IDE - Mendix Studio Pro。

    HpaPaaS(高生产力应用PaaS)

    上文提到,“Low-Code”一词是拜Forrester所赐。作为同样是国际知名调研机构(a.k.a 造词小能手)的Gartner,显然不会轻易在这场可能决定低代码领域江湖地位的新概念作词大赛中认输,于是也于2017年发明了“HpaPaaS”(High-productivity application Platform as a Service)这个听上去更高大上的缩写词。

    按照Gartner的定义,HpaPaaS是一种支持声明式、模型驱动设计和一键部署的平台,提供了云上的快速应用开发(RAD)、部署和运行特性;这显然与低代码的定义如出一辙。但事实证明,名字起得太专业并不见得是好事,“HpaPaas”最终还是败给了起源更早、更接地气也更顺口的“Low-Code”:从2019年开始,Gartner在其相关调研报告中也开始全面采用“Low-Code”一词(如LCAP),亲手为“HpaPaaS”打上了 @deprecated 印记。

     

     

    图源:https://blog.kintone.com/business-with-heart/difference-saas-iaas-paas-apaas-hpapaas

    值得补充的是,“HpaPaaS“这个词也并非横空出世,而是传承自更早之前Gartner提出的“aPaaS”,它俩之间的关系是:HpaPaaS只是aPaaS的一个子类;除了HpaPaaS这种通过低代码实现的高生产力应用开发平台以外,aPaaS还包括面向纯代码的传统应用开发平台(High-control aPaaS,即可控度更高的纯代码开发方式)。

    不值得但就想八卦一下的是,“aPaaS”这个词也非凭空捏造,而是与云计算的兴起渊源颇深。相信各位云道中人都已猜到,aPaaS与IaaS/PaaS/SaaS这些云计算远古概念是一脉相承的:aPaaS介于PaaS和SaaS之间,相比PaaS提供的服务更偏应用,但又不像SaaS一样提供现成的软件服务(更详细的说明可参考配图来源文章)。

    四 为什么需要低代码

    低代码是什么可能并没那么重要,毕竟在这个信息爆炸的世界,永远不缺少新奇而又短命的事物。大部分所谓的新技术都只是昙花一现:出现了,被看到了;大部分人“哦”了一声,已阅但表示不感兴趣;小部分人惊叹于它的奇思妙想,激动地点了个赞后,回过头来该用什么还是什么。真正决定新技术是否能转化为新生产力的,永远不是技术本身有多么优秀和华丽,而是它是否真的被需要,即:为什么需要低代码?如果用不同的主语填充上面这个问句(冷知识:这叫做“延迟主语初始化”),可以更全面地看待这个问题:

    为什么「市场」需要低代码?

    在这个大爷大妈都满嘴“互联网+”和“数字化转型”的时代,企业越来越需要通过应用(App)来改善企业内部的信息流转、强化与客户之间的触点连接。然而,诞生还不太久的IT信息时代,也正面临着与我国社会主义初级阶段类似的供需关系矛盾:落后的软件开发生产力跟不上人民日益增长的业务需求。

     

    是的,低代码正在将应用软件开发过程工业化:每个低代码开发平台都是一个技术密集型的应用工厂,所有项目相关人员都在同一条产线内紧密协作。开发主力不再是熟知for循环一百种写法的技术Geek,而是一群心怀想法业务sense十足的应用Maker。借助应用工厂中各种成熟的基础设施、现成的标准零件、自动化的装配流水线,开发者只需要专注于最核心的业务价值即可。即便是碰到非标需求,也可以随时自己动手,用最灵活的手工定制(代码)方式来解决各种边角问题。

    扩大应用开发劳动力

    通过让大部分开发工作可以仅通过简单的拖拽与配置完成,低代码(包括零代码)显著降低了使用者门槛,让企业能够充分利用前面所提到的平民开发者资源。部分纯零代码需求场景下,低代码还能让业务人员实现自助式(self-service)应用交付,既解决了传统IT交付模式下的任务堆积(backlog)问题,避免稀缺的专业开发资源被大量简单、重复性的应用开发需求所侵占,也能让业务人员真正按自己的想法去实现应用,摆脱交由他人开发时不可避免的桎梏。

     

    而如今这个时代,支撑低代码的那些“老”技术都已经过长时间的发展酝酿与市场检验,而另一些完美互补的“新”技术(e.g. 云原生、响应式Web)也在飞速发展和走向成熟,是时候通过“低代码”这个新酒瓶重新包装上市,为亟需新生产力的传统IT市场带来一场真香之旅了。

    业务收益不明显

    即使十几年前的低代码技术已经足够成熟,也一定不会在当年的应用开发市场上产生现在这样的影响力。为什么?因为技术都是为业务服务的,而当时的应用开发业务需求可比现在简单多了:没有如今的多渠道(Multi-channel)、多样化体验(Multi-experience)和各种集成与定制需求,也不会奢求如今已成为企业级应用标配的弹性、分布式和高可用,更是缺乏快速变化的IT业务场景来推动持续集成与快速交付。

    虽然低代码可以完美解决上述所有问题(e.g. 多端应用生成、云原生架构、API集成能力),但放在当年的市场和业务背景下,加上前面所说的技术不成熟度,整体的投入产出比会很低,不足以让企业大面积采纳低代码解决方案。

     

    而如今这个时代,企业都快被新技术带来的能力和收益“惯坏了”,动不动就是:我想做一个送菜应用。用户端?安卓、iOS、H5、小程序都来一套。运营端?一般都在电脑上看,但记得手机上也得适配啊。服务端?上云,必须的。哦,我听技术合伙人说现在流行多云架构,也给我整一套哈。运维还要钱?啥是运维?应用有了不就能用了嘛,运维还要花我钱?你当投资者给我的钱是大风刮来的啊!

    如果用传统的开发模式,这么全套下来的工时与报价,可能早就吓跑了这群跟产品经理一样天真可爱的人;但现代化的低代码技术,可以圆了上面这位创业者的卖菜梦,用白菜一般的价格,实现白粉一样的价值。当年的程维如果能用上现在的低代码,第一版的滴滴App也就不至于被外包做得乌烟瘴气直接报废了(至少能多扛一阵子...)。

    为什么「专业开发者」也需要低代码?

    虽然零代码确实是设计给非专业开发者用的,但其所能支撑的业务场景确实有限,无法真正革新传统开发模式,替代那些仍需专业开发者参与的复杂业务场景。而狭义上的低代码却有潜力做到这一点,因为它天生就是为专业开发者而量身定制的。Gartner最近的一项调研报告显示,“66%的低代码开发平台用户都是企业IT部门的专业开发者”。这充分说明了,专业开发者比平民开发者更需要低代码。

    屏幕前一批穿格子衬衫的同学要发问了:“低代码都不怎么写代码了,怎么能算是为我们程序员服务呢?”。虽然程序员讨厌重复自己,但重要的事情还是得多说一遍:开发 ≠ 写代码。1万年前蹲在洞穴里的原始人,在用小石子画远古图腾;100年前坐在书桌前的徐志摩,在用钢笔给林徽因写情书;而今天趴在屏幕前的很多人,相信都已经开始用上手写板或iPad涂涂写写了。千百年来,人类使用的工具一直在演进,但所从事活动的本质并没有多大改变。无论是用小石子还是小鼠标,写作绘画的本质都是创造与表达,最终作品的好坏并不取决于当时你手中拿着什么;同样地,应用开发的本质是想法和逻辑,最终价值的高低也不取决你实现时是用的纯代码还是低代码。

    而相比纯代码而言,低代码极有可能成为更好的下一代生产力工具:

    减少不必要的工作量

    可视化拖拽与参数配置的极简开发模式,结合模型驱动的代码自动生成机制,可以消灭绝大部分繁琐和重复的boilerplate代码;一站式的部署和运维管理平台,无需自己搭建CI/CD流水线、申请环境资源、配置监控报警;一次搭建同时生成、构建和发布多端应用,免去人工同步维护多个功能重复的端应用;开箱即用的组件库、模板库、主题库、连接器等,让最大化软件复用成为可能。总而言之,低代码能够让专业开发者更专注于创新性、有价值、有区分度的工作,而不是把宝贵开发时间都耗费在上面那些不必要的非业务核心工作上。

    强大的平台能力支撑

    虽然上面列的技术支撑性工作并不直接产生业务价值,但却会直接影响业务的性能、成本、稳定性、安全性、可持续发展能力等。有远见的企业,绝不允许牺牲这些重要指标,来换取短暂的业务加速。低代码开发平台深知这一点,因此在简化和屏蔽底层技术细节的同时,也会尽可能把自己所cover的部分做到最好(至少能和纯代码开发方式一样好),包括但不限于:

    • 现代化的技术架构和实现:现代化的低代码开发平台,在支撑用户应用时所选择的技术架构与实现方案,也会是现代化且符合业界最佳实践的,例如,前端基于主流的HTML5/CSS3标准和React框架,后端基于成熟的Java语言、SpringBoot框架和MySQL数据库,部署环境基于云原生的Docker镜像、CI/CD流水线、K8s集群和Service Mesh技术(相关知识可参考《正确入门Service Mesh:起源、发展和现状》)。
    • 零成本的技术升级和维护:低代码的高维抽象开发方式,让应用的核心业务逻辑与底层技术细节彻底解耦。开发者在大部分情况下都不需要关心底层技术选型,同时也无需亲自跟进这些技术的版本升级与漏洞修复,免费享受与时俱进的技术红利和应用安全性提升。即便遇到某些底层技术或工具需要进行彻底更换(比如不再维护的开源项目),开发者也完全不必感知;技术迁移再费劲再难搞,平台自己努力就行,对开发者来说只要服务一直在线,岁月就依然静好;事后可能还会惊喜地发现,应用访问突然就变得更快了,仿佛冥冥中自有天助,感激上苍和低代码。

    一体化生态能力复用

    复用(Reuse)是提升软件开发效率和工程质量的最有效途径。传统的代码开发模式下,开发者可以通过提取公共类/函数、引用共享库、调用外部API服务、沉淀代码片段和模板等方式实现复用。在低代码的世界里,平台也可以提供对应的多层次多粒度复用手段,比如页面组件库、逻辑函数库、应用模板库等。

    但更重要的是,低代码平台还可以充分发挥其一体化的生态优势,提供强大易用的可复用能力(资产)的发现、集成与共享体系:以页面组件为例,你可以直接用系统组件,也可以在平台自带的组件市场上搜索和引用更合适的组件,还可以自己用代码开发一个自定义组件并发布到市场中。平台的生态体系越大,积累的可复用能力就越多,应用的开发成本也会越低。

    相比而言,虽然传统代码世界整体生态更庞大和深厚,但由于各类技术不互通、缺乏统一平台与市场、代码集成成本高等原因,一直以来都没有形成有类似规模潜力的生态能力复用体系,导致重复造轮子和低水平重复建设的现象司空见惯,还美名为“新基建”。

    说到这里,另一批裹着冲锋衣头顶锃亮的同学也忍不住了:“万一低代码真的发展起来了,是不是就不需要那么多程序员了啊?上有老下有小的,同是码农身,相煎何太急!”。低代码虽然是一场应用开发生产力革命,但并不会革掉程序员的饭碗。它去掉的只是难懂的编程语法、繁琐的技术细节和一切可自动化的重复性工作,并没有也无法去掉应用开发最核心的东西:严谨的业务逻辑、巧妙的算法设计、良好的工程风格等。对于真正的程序员,即使剥去他一层又一层的编程语言和工具熟练度技能外壳,最终剩下的仍然是一个有价值的硬核开发者。

    当然,如果你坚持要用纯粹的写代码方式来改变世界,也不至于失业。要么,你可以选择那些低代码暂时不太适用的领域,比如底层系统驱动、3D游戏引擎、火箭发射程序;或者,你也可以选择去写低代码中那一部分不可或缺的自定义代码扩展,为平民开发者提供高质量的积木。最后,你也完全可以选择为低代码平台本身的底层代码添砖加瓦,比如加入阿里云云原生应用研发平台EMAS团队 (〃'▽'〃) ,与作者一起共建下一代云原生低代码开发平台“Mobi”,内推直达邮箱:pengqun.pq # http://alibaba-inc.com

    为什么「我不」需要低代码

    即使所有人都认同上述“为什么要用低代码”的理由,但仍不时会有试水者跳出来,给大家细数“为什么我不需要低代码”。实践出真知没错,而且大部分质疑背后也都有一定道理;但在我看来,更多的可能是主观或无意识的偏见。这里我列了一些对低代码的常见质疑和我个人的看法,期望能帮助大家看到一个更全面和客观的低代码。

    质疑1:低代码平台不好使

    “试用过一些所谓的低代码开发平台,要么能力很弱,要么体验太差,只能开发点玩具应用。”

    作为调研过国内外多款低代码产品的深度体验用户,我的观点是:不能以偏概全。低代码市场在国内正处于爆发初期,所以许多与低代码只沾一点边的产品也都在蹭热点;但它们并不能代表低代码目前的业界水平和发展方向。市面上真正成熟的企业级低代码开发平台,完全有能力以高效的开发方式满足大部分复杂场景的功能需求,以及企业级应用所需要的安全、性能、可伸缩等非功能需求,这一点在国外市场已得到充分验证(不然也不会这么被寄予厚望)。

    当然,国内市场尚处于鱼龙混杂的混战阶段,遇到真龙的概率很低,但碰上金鱼鲤鱼甚至木头假鱼都在所难免。相信随着时间推移,真正有实力和口碑的产品都能脱颖而出,为大家展现低代码该有的样子。

    质疑2:低代低开发不可控

    “平台上的各种可视化组件、逻辑动作和部署环境都是黑盒,如果内部出问题无法排查和解决。”

    作为同样不搞清楚底层原理不舒服斯基的程序员,我更愿意相信:问题只是暂时的。虽然这确实是目前使用低代码平台时绕不开的一个痛点,但并不属于低代码技术本身的固有缺陷。计算机领域有一句至理名言:任何问题都可以通过增加一个间接的中间层来解决。低代码的思路亦是如此:与当年的操作系统和现在的云平台一样,都是想通过建立一个黑盒化的中间层抽象来降低开发者的工作量与心智负担。

    当然,所有额外增加的中间层都不是完全免费的,低代码也不例外。作为一个尚未成熟稳定的新的中间层,低代码必然会出现各种让使用者束手无措的问题,就跟当年的操作系统内核bug、如今的云主机I/O hang一样。但历史规律也告诉我们,所有伟大的技术最终都会走向成熟;只要低代码领域一直健康发展,问题总会越来越少,最终降到一个绝大部分人感知不到的范围内。过去萦绕在Windows用户心中挥之不去的“蓝屏”问题,对如今的新用户来说早已不知为何物;今天低代码开发者所遇到的种种“蓝瘦”问题,未来也终将成为被遗忘的历史(谁还没段黑历史呢)。

    质疑3:低代码应用难维护

    “应用一旦复杂起来,各种复杂逻辑流穿插着自定义代码,看不懂也改不动,还不如全用代码呢。”

    作为对软件可维护性深有感触的无脑级布道者(见《救火必备!问题排查与系统优化手册》),我不得不说:用低代码开发,也要讲基本法。一般来说,无论是使用低代码开发还是纯代码开发,造成应用可维护性低的根本原因往往不在于开发工具,而是开发者自身没有去遵循一些软件开发的普适原则,比如工程规范性、命名可读性、DRY/KISS/SOLID原则等。

    好的低代码平台绝不会阻碍开发者去改善应用的可维护性;恰恰相反,还会尽可能提供引导和帮助。以Mendix为例,除了支持基本的模型分析与重构(e.g. 无用模型、对象重命名、子逻辑流提取)以外,甚至还提供了基于ISO/IEC 25010标准的应用质量监控(AQM)能力。另一方面,让应用变得难以维护的一个客观原因也是应用本身过于复杂,而低代码作为高度抽象和自动化的开发模式,在降低应用复杂度方面是专业的。

    综合来看,低代码虽然不是能解决一切问题的银弹,但更不是会带来更多问题的炸弹:在提高应用可维护性方面的上限,一定比传统开发模式更高;但决定应用可维护性下限的,依然还是开发者自己。

    五 低代码行业发展

    回应质疑的最好方式,就是做好你自己,用实际的表现说话。对于一个行业而言,判断它当前的表现是否够好,或者未来是否有潜力做到更好,可以从以下这三个方面进行衡量:市场规模(蛋糕够不够大)、适用场景(是否可落地)、竞品状况(有没有被验证过)。

    市场规模

    "Talk is cheap,show me the code money." —— Linus Starcraft

    文章可以忽悠,但市场不会说谎:

    • Forrester在2015年曾预测过,低代码的市场将从2015年的17亿美元增长至2020年的150亿美元。
    • Marketsandmarkets在今年四月份的分析报告中预测,低代码的市场将从2020年的130亿美元(估算值,可以看出来与Forrester当年的预测是接近的)增长到2025年的450亿美元(年复合增长率:28.1%)。
    • PS Inteligence在2018年的分析报告中预测,全球的低代码开发平台市场中,亚太地区将在今后五年(2019-2024年)中保持最高的增长速度。

    总结一下就是两点:

    • 低代码的市场规模足够大,且一直都在高速增长。
    • 作为亚太地区的经济大国与IT强国,中国的低代码市场将会引来一个爆发期,未来几年内的增速都会超过全球平均水平。

    适用场景

    理论上来说,低代码是完全对标传统纯代码的通用开发模式,应该有能力支撑所有可能的业务场景。但理论也只是理论,低代码一统江湖的梦想尚未照进现实,也不可能完全取代现实。前文中提到过,低代码与纯代码方式是互补关系,未来也将长期共存,各自在其所适合的业务场景中发光发热。同时还需要指出的是,当前阶段的低代码技术、产品和市场都尚未完全成熟,因此部分本来可能很适合用低代码来开发的场景,目前也只能先用纯代码来替代。

    Gartner在2019年的低代码调研报告中,曾经绘制过一张用来阐述低代码适用场景的“应用金字塔”:

    • 应用级别划分:从下往上,分别为工作组级(Workgroup Class)、部门级(Departmental Class)、企业级(Enterprise Class)、可扩展需求极强的企业级(Extreme-Scale Enterprise Class)。容易看出来,它主要的划分维度就是应用所面向的用户基数(基数越大,可扩展需求也越高)。
    • 任务关键性:从下往上,各级别应用的任务关键性(Mission Criticality)逐级递增。例如一个只在工作组内使用的后台管理应用,一般都不会涉及到影响整个企业的关键任务。脱离企业这个视角来看,整个软件产业中也有很多通用的任务关键型应用,比如:实时操作系统、航空调度系统、银行对账系统。
    • 实现复杂度:从下往上,各级别应用的复杂度(Complexity)也逐级递增。例如最上层的企业级应用,除了功能覆盖面大导致业务复杂以外,往往还需要满足更多苛刻的非功能需求,包括但不限于:用户体验、性能、可靠性、安全性、可伸缩性、可维护性、兼容性。其他一些复杂软件的案例包括:3D游戏界面(交互复杂)极其底层的游戏引擎(逻辑复杂)、超大型CRM系统(一方面是实现很复杂,另一方面,这种成熟软件的标准化程度较高,大部分情况下可以直接用现成的SaaS软件)。
    • 应用需求量:从上往下,各级别应用的需求体量(Volume)逐级递增,呈现一个金字塔形状。这个特征可以用万能的2/8原则来理解:20%的“全民”应用,由于需求的通用性和普适性,可以覆盖至少80%的用户群体(例如企业大部分人都要用的考勤系统);而剩下那80%的“小众”应用,由于需求的定制化和特殊性(例如蚂蚁的期权系统...),就只能覆盖各自小圈子里那20%的用户了。
    • 与低代码的契合关系:从上往下,各级别应用与低代码越来越契合(Relevant)。也就是说:越简单的应用,越契合低代码;越不太关键的任务,也越契合低代码。同时,由于契合低代码的应用更偏金字塔底层,而这些应用的需求量都更大,所以可以得出如下判断:低代码能够适用于大部分业务场景(而且这个比例会一直上升,逐步往金字塔的更上层应用逼近),例如:B2E类应用(表单、审批流、ERP系统)、B2B类应用(企业商城、工业控制台)、B2C类应用(企业展示、营销页、店铺装修)。

    竞品概况

    低代码虽然是一个新兴概念,但这个行业本身并不算很新(前文也有提到),这些年以来早就积累了不少资深的荣耀王者。同时,低代码作为一个朝阳产业和资本热点,近几年也不断有更多的新玩家在加入这个刺激战场。

    上图分别是Gartner给出的低代码平台魔力象限和Forrester给出的低代码平台技术波谱。从图中可以看到:

    • OutSystems和Mendix一马当先,是公认的低代码领域头牌。这两家都是很纯粹的通用低代码开发平台,且都经过了长时间的发展和积累:OutSystems成立于2001年,员工人数1000+,年营收超过1亿美元;2018年6月获得了KKR和高盛的3.6亿美元融资,目前估值超过10亿美元;Mendix成立于2005年,员工人数500+,年营收超过2300万美元(18年数据),2018年8月被西门子以7.3亿美元收购。
    • Salesforce和Microsoft紧随其后,都处于行业领先者地位。但这两家的公司性质和发展路径都很不一样:Salesforce是以SaaS起家,公司规模就不用多说了,反正就是SaaS届的巨无霸。这类SaaS厂商做低代码的动力,是为了解决客户对成品SaaS软件的定制诉求。M$更不用多介绍,只说下他们做低代码的天然优势:一方面,作为办公软件航空母舰,低代码可以帮助他们的客户实现从Excel表单到定制App的能力与体验升级;另一方面,作为云计算三巨头之一,低代码可以帮助他们连接内部的云计算生态体系,为开发者提供一个统一和易用的上云界面。
    • 国外市场已经得到充分验证,但国内市场还刚刚兴起,还没有一家能够赢得上述调研机构的芳心,挤进上面这两张方图。国内目前的一些竞品和融资情况包括:2018年5月,搭搭云完成A轮的千万级融资;2018年9月,宜创科技得到清源创投的战略融资;2018年12月,轻流完成千万级Pre-A融资;2019年8月,数式科技得到盈动资本的数千万人民币天使轮融资;2019年8月,ClickPaas获得晨兴资本数百万美元的A轮融资;2019年,奥哲分别获得阿里5千万的A+轮融和高榕资本上亿元的B轮融资。(注:竞品数据来源于我们组PD的辛勤整理;为此我决定这篇文章剩下内容再也不黑PD了;下篇再说。)

    六 结语

    本文总结了低代码领域的基本概念、核心价值与行业现状。虽然这些内容都比较基础和偏理论,但我始终认为,深刻理解一个系统的前提,正是这些务虚的东西 —— 技术架构只会告诉你这个系统是怎么实现的(How),无法准确表述它到底能用来做什么(What),以及为什么要做这样一个东西(Why);而后面这两个问题的答案,才是后续系统所有设计与演进的根因和驱动力。

    原文链接: https://cloud.tencent.com/developer/article/1748264

    展开全文
  • 《ROS理论与实践》学习笔记(五)构建机器人仿真平台课程内容1.优化物理仿真模型1.使用xacro模型文件进行机器人建模2. ros_control3.仿真模型的优化4.创建物理仿真环境本讲作业1.将上讲urdf模型改写为xacro文件,...

    在学习《ROS理论与实践》课程时,记录了学习过程中的编程练习,课后作业以及发现的问题,后续会对尚未解决的问题继续分析并更新,纯小白,仅供参考。
    本次学习笔记关于课程中的第六讲:构建机器人仿真平台 。主要学习了ROS的xacro建模方法和gazebo仿真。


    课程内容

    1.优化物理仿真模型

    1.使用xacro模型文件进行机器人建模

    xacro模型文件更加简洁,提供可编程接口

    • 常量定义
      < xacro:property name=“xxx” value=“xxx”/>
    • 宏定义
      < xacro:macro name=“xxx” params=“xxx”>

      < /xacro:macro>
    • 宏调用
      < name A=“xxx” B=“xxx” C=“xxx”/>
    • 文件包含
      < xacro:include filename="$(find 功能包名称)/路径/xxx.xacro"/>

    2. ros_control

    ros_control提供了一系列控制器接口、传动装置接口、硬件接口、控制器工具箱等
    控制器(controllers)主要包括:

    • joint_state_controller
    • joint_effort_controller
    • joint_position_controller
    • joint_velocity_controller

    具体内容参考ROS Wiki链接:ros_control

    3.仿真模型的优化

    1. 为link添加惯性参数和碰撞属性
      添加< collision >以及< cylinder_inertial_matrix>标签
    2. 为link添加gazebo标签
      添加< gazebo>标签
    3. 为joint添加传动装置
      添加< transmission>标签
      具体内容参考ROS Wiki链接:urdf_transmission
    4. 添加gazebo控制器插件
      · < robotNamespace > 机器人命名空间
      · < leftJoint>< rightJoint> 左右轮转动的关节joint
      · < wheelSeparation>< wheelDiameter> 机器人模型尺寸,用于计算差速参数
      · < commandtTopic> 控制器订阅的速度控制指令
      · < odometryFrame> 里程计数据的参考坐标系

    2.创建物理仿真环境

    方法:

    • 直接添加环境模型
    • 使用building editor

    本讲作业

    1.将上讲urdf模型改写为xacro文件,进行运动控制仿真

    • 基本xacro模型文件nmybot_base_gazebo.xacro
    <?xml version="1.0"?>
    <robot name="mybot" xmlns:xacro="http://www.ros.org/wiki/xacro">
    
        <!-- PROPERTY LIST -->
        <xacro:property name="M_PI" value="3.1415926"/>
        <xacro:property name="base_mass"   value="20" /> 
        <xacro:property name="base_radius" value="0.20"/>
        <xacro:property name="base_length" value="0.05"/>
    
        <xacro:property name="motor_mass"   value="0.01" />
        <xacro:property name="motor_radius" value="0.04"/>
        <xacro:property name="motor_length" value="0.18"/>
        <xacro:property name="motor_joint_x" value="0.1"/>
        <xacro:property name="motor_joint_y" value="0.11"/>
    
        <xacro:property name="wheel_mass"   value="50" />                 <!-- to avoid the auto slip -->
        <xacro:property name="wheel_radius" value="0.06"/>
        <xacro:property name="wheel_length" value="0.025"/>
        <xacro:property name="wheel_joint_y" value="0.1025"/>
        <xacro:property name="wheel_joint_z" value="0"/>
    
        <xacro:property name="caster_mass"    value="0.5" /> 
        <xacro:property name="caster_radius"  value="0.0175"/>             <!-- wheel_radius - ( base_length/2 - wheel_joint_z) -->
        <xacro:property name="caster_length"  value="0.01"/>
        <xacro:property name="caster_joint_x" value="0.18"/>
        <xacro:property name="caster_joint_z" value="-0.025"/>
        <xacro:property name="caster_link_z"   value="-0.0175"/>
    
        <!-- Defining the colors used in this robot -->
        <material name="yellow">
            <color rgba="1 0.4 0 1"/>
        </material>
        <material name="black">
            <color rgba="0 0 0 0.95"/>
        </material>
        <material name="gray">
            <color rgba="0.75 0.75 0.75 1"/>
        </material>
        
        <!-- Macro for inertia matrix -->
        <xacro:macro name="sphere_inertial_matrix" params="m r">
            <inertial>
                <mass value="${m}" />
                <inertia ixx="${2*m*r*r/5}" ixy="0" ixz="0"
                    iyy="${2*m*r*r/5}" iyz="0" 
                    izz="${2*m*r*r/5}" />
            </inertial>
        </xacro:macro>
    
        <xacro:macro name="cylinder_inertial_matrix" params="m r h">
            <inertial>
                <mass value="${m}" />
                <inertia ixx="${m*(3*r*r+h*h)/12}" ixy = "0" ixz = "0"
                    iyy="${m*(3*r*r+h*h)/12}" iyz = "0"
                    izz="${m*r*r/2}" /> 
            </inertial>
        </xacro:macro>
    
        <!-- Macro for robot  motor -->
        <xacro:macro name="motor" params="prefix reflect">
            <joint name="${prefix}_motor_joint" type="fixed">
                <origin xyz="${-motor_joint_x} ${reflect*motor_joint_y} 0" rpy="0 0 0"/>
                <parent link="base_link"/>
                <child link="${prefix}_motor_link"/>
                <axis xyz="0 1 0"/>
            </joint>
    
            <link name="${prefix}_motor_link">
                <visual>
                    <origin xyz="0 0 0" rpy="${M_PI/2} 0 0" />
                    <geometry>
                        <cylinder radius="${motor_radius}" length = "${motor_length}"/>
                    </geometry>
                    <material name="gray" />
                </visual>
                <collision>
                    <origin xyz="0 0 0" rpy="${M_PI/2} 0 0" />
                    <geometry>
                        <cylinder radius="${motor_radius}" length = "${motor_length}"/>
                    </geometry>
                </collision>
                <cylinder_inertial_matrix  m="${motor_mass}" r="${motor_radius}" h="${motor_length}" />
            </link>
    
            <gazebo reference="${prefix}_motor_link">
                <material>Gazebo/Gray</material>
            </gazebo>
    
            <!-- Transmission is important to link the joints and the controller -->
            <transmission name="${prefix}_motor_joint_trans">
                <type>transmission_interface/SimpleTransmission</type>
                <joint name="${prefix}_motor_joint" >
                    <hardwareInterface>hardware_interface/VelocityJointInterface</hardwareInterface>
                </joint>
                <actuator name="${prefix}_motor_joint_motor">
                    <hardwareInterface>hardware_interface/VelocityJointInterface</hardwareInterface>
                    <mechanicalReduction>1</mechanicalReduction>
                </actuator>
            </transmission>
        </xacro:macro>
    
        <!-- Macro for robot wheel -->
        <xacro:macro name="wheel" params="prefix reflect">
            <joint name="${prefix}_wheel_joint" type="continuous">
                <origin xyz="0 ${reflect*wheel_joint_y} ${-wheel_joint_z}" rpy="0 0 0"/>
                <parent link="${prefix}_motor_link"/>
                <child link="${prefix}_wheel_link"/>
                <axis xyz="0 1 0"/>
            </joint>
    
            <link name="${prefix}_wheel_link">
                <visual>
                    <origin xyz="0 0 0" rpy="${M_PI/2} 0 0" />
                    <geometry>
                        <cylinder radius="${wheel_radius}" length = "${wheel_length}"/>
                    </geometry>
                    <material name="gray" />
                </visual>
                <collision>
                    <origin xyz="0 0 0" rpy="${M_PI/2} 0 0" />
                    <geometry>
                        <cylinder radius="${wheel_radius}" length = "${wheel_length}"/>
                    </geometry>
                </collision>
                <cylinder_inertial_matrix  m="${wheel_mass}" r="${wheel_radius}" h="${wheel_length}" />
            </link>
    
            <gazebo reference="${prefix}_wheel_link">
                <material>Gazebo/Gray</material>
            </gazebo>
    
            <!-- Transmission is important to link the joints and the controller -->
            <transmission name="${prefix}_wheel_joint_trans">
                <type>transmission_interface/SimpleTransmission</type>
                <joint name="${prefix}_wheel_joint" >
                    <hardwareInterface>hardware_interface/VelocityJointInterface</hardwareInterface>
                </joint>
                <actuator name="${prefix}_wheel_joint_motor">
                    <hardwareInterface>hardware_interface/VelocityJointInterface</hardwareInterface>
                    <mechanicalReduction>1</mechanicalReduction>
                </actuator>
            </transmission>
        </xacro:macro>
    
        <!-- Macro for robot caster -->
        <xacro:macro name="caster" params="prefix reflect">
            <joint name="${prefix}_caster_joint" type="continuous">
                <origin xyz="${caster_joint_x} 0 ${caster_joint_z+caster_link_z}" rpy="0 0 0"/>
                <parent link="base_link"/>
                <child link="${prefix}_caster_link"/>
                <axis xyz="0 1 0"/>
            </joint>
    
            <link name="${prefix}_caster_link">
                <visual>
                    <origin xyz="0 0 0" rpy="0 0 0"/>
                    <geometry>
                        <sphere radius="${caster_radius}" />
                    </geometry>
                    <material name="black" />
                </visual>
                <collision>
                    <origin xyz="0 0 0" rpy="0 0 0"/>
                    <geometry>
                        <sphere radius="${caster_radius}" />
                    </geometry>
                </collision>      
                <sphere_inertial_matrix  m="${caster_mass}" r="${caster_radius}" />
            </link>
    
            <gazebo reference="${prefix}_caster_link">
                <material>Gazebo/Black</material>
            </gazebo>
        </xacro:macro>
    
        <!-- Macro for robot base-->
        <xacro:macro name="mybot_base_gazebo">
            <link name="base_footprint">
                <visual>
                    <origin xyz="0 0 0" rpy="0 0 0" />
                    <geometry>
                        <box size="0.001 0.001 0.001" />
                    </geometry>
                </visual>
            </link>
            <gazebo reference="base_footprint">
                <turnGravityOff>false</turnGravityOff>
            </gazebo>
    
            <joint name="base_footprint_joint" type="fixed">
                <origin xyz="0 0 ${base_length/2 + caster_radius*2}" rpy="0 0 0" />        
                <parent link="base_footprint"/>
                <child link="base_link" />
            </joint>
    
            <link name="base_link">
                <visual>
                    <origin xyz=" 0 0 0" rpy="0 0 0" />
                    <geometry>
                        <cylinder length="${base_length}" radius="${base_radius}"/>
                    </geometry>
                    <material name="yellow" />
                </visual>
                <collision>
                    <origin xyz=" 0 0 0" rpy="0 0 0" />
                    <geometry>
                        <cylinder length="${base_length}" radius="${base_radius}"/>
                    </geometry>
                </collision>   
                <cylinder_inertial_matrix  m="${base_mass}" r="${base_radius}" h="${base_length}" />
            </link>
    
            <gazebo reference="base_link">
                <material>Gazebo/Blue</material>
            </gazebo>
    
            <motor prefix="left"  reflect="-1"/>
            <motor prefix="right" reflect="1"/>
    
            <wheel prefix="left"  reflect="-1"/>
            <wheel prefix="right" reflect="1"/>
    
            <caster prefix="front" reflect="1"/>
    
            <!-- controller -->
            <gazebo>
                <plugin name="differential_drive_controller" 
                        filename="libgazebo_ros_diff_drive.so">
                    <rosDebugLevel>Debug</rosDebugLevel>
                    <publishWheelTF>true</publishWheelTF>
                    <robotNamespace>/</robotNamespace>
                    <publishTf>1</publishTf>
                    <publishWheelJointState>true</publishWheelJointState>
                    <alwaysOn>true</alwaysOn>
                    <updateRate>100.0</updateRate>
                    <legacyMode>true</legacyMode>
                    <leftJoint>left_wheel_joint</leftJoint>
                    <rightJoint>right_wheel_joint</rightJoint>
                    <wheelSeparation>${wheel_joint_y*2}</wheelSeparation>
                    <wheelDiameter>${2*wheel_radius}</wheelDiameter>
                    <broadcastTF>1</broadcastTF>
                    <wheelTorque>30</wheelTorque>
                    <wheelAcceleration>1.8</wheelAcceleration>
                    <commandTopic>cmd_vel</commandTopic>
                    <odometryFrame>odom</odometryFrame> 
                    <odometryTopic>odom</odometryTopic> 
                    <robotBaseFrame>base_footprint</robotBaseFrame>
                </plugin>
            </gazebo> 
        </xacro:macro>
    
    </robot>
    

    基础模型xacro文件nmybot_gazebo.xacro

    <?xml version="1.0"?>
    <robot name="mybot" xmlns:xacro="http://www.ros.org/wiki/xacro">
    
        <xacro:include filename="$(find mybot_description)/urdf/nmybot_base_gazebo.xacro" />
    
        <mybot_base_gazebo/>
    
    </robot>
    

    launch文件

    <launch>
    	<arg name="model" default="$(find xacro)/xacro --inorder '$(find mybot_description)/urdf/nmybot_gazebo.xacro'" />
    	<arg name="gui" default="true" />
    
    	<param name="robot_description" command="$(arg model)" />
    
        <!-- 设置GUI参数,显示关节控制插件 -->
    	<param name="use_gui" value="$(arg gui)"/>
    
        <!-- 运行joint_state_publisher节点,发布机器人的关节状态  -->
    	<node name="joint_state_publisher" pkg="joint_state_publisher" type="joint_state_publisher" />
    
    	<!-- 运行robot_state_publisher节点,发布tf  -->
    	<node name="robot_state_publisher" pkg="robot_state_publisher" type="robot_state_publisher" />
    
        <!-- 运行rviz可视化界面 -->
    	<node name="rviz" pkg="rviz" type="rviz" args="-d $(find mybot_description)/config/mybot_xacro.rviz" required="true" />
    
    </launch>
    
    
    • 加入摄像头及Kinect后的xacro模型文件nmybot_all_gazebo.xacro,其中摄像头及Kinect模型文件来自课程代码包,不再贴出
    <?xml version="1.0"?>
    <robot name="arm" xmlns:xacro="http://www.ros.org/wiki/xacro">
    
        <xacro:include filename="$(find mybot_description)/urdf/nmybot_base_gazebo.xacro" />
        <xacro:include filename="$(find mybot_description)/urdf/sensors/camera_gazebo.xacro" />
        <xacro:include filename="$(find mbot_description)/urdf/sensors/kinect_gazebo.xacro" />
    
        <xacro:property name="camera_offset_x" value="0.17" />
        <xacro:property name="camera_offset_y" value="0" />
        <xacro:property name="camera_offset_z" value="0.025" />
    
        <xacro:property name="kinect_offset_x" value="0.11" />
        <xacro:property name="kinect_offset_y" value="0" />
        <xacro:property name="kinect_offset_z" value="0.06" />
    
        <!-- Camera -->
        <joint name="camera_joint" type="fixed">
            <origin xyz="${camera_offset_x} ${camera_offset_y} ${camera_offset_z}" rpy="0 0 0" />
            <parent link="base_link"/>
            <child link="camera_link"/>
        </joint>
    
        <!-- kinect -->
        <joint name="kinect_joint" type="fixed">
            <origin xyz="${kinect_offset_x} ${kinect_offset_y} ${kinect_offset_z}" rpy="0 0 0" />
            <parent link="base_link"/>
            <child link="kinect_link"/>
        </joint>
    
        <xacro:kinect_camera prefix="kinect"/>
        <xacro:usb_camera prefix="camera"/>
    
        <mybot_base_gazebo/>
    
    </robot>
    

    Rviz_launch文件类似基础模型,不再贴出
    Gazebo_launch文件view_mybot_gazebo_world.launch

    <launch>
    
        <!-- 设置launch文件的参数 -->
        <arg name="world_name" value="$(find mybot_gazebo)/worlds/playground.world"/>
        <arg name="paused" default="false"/>
        <arg name="use_sim_time" default="true"/>
        <arg name="gui" default="true"/>
        <arg name="headless" default="false"/>
        <arg name="debug" default="false"/>
    
        <!-- 运行gazebo仿真环境 -->
        <include file="$(find gazebo_ros)/launch/empty_world.launch">
            <arg name="world_name" value="$(arg world_name)" />
            <arg name="debug" value="$(arg debug)" />
            <arg name="gui" value="$(arg gui)" />
            <arg name="paused" value="$(arg paused)"/>
            <arg name="use_sim_time" value="$(arg use_sim_time)"/>
            <arg name="headless" value="$(arg headless)"/>
        </include>
    
        <!-- 加载机器人模型描述参数 -->
        <param name="robot_description" command="$(find xacro)/xacro --inorder '$(find mybot_description)/urdf/nmybot_all_gazebo.xacro'" /> 
    
        <!-- 运行joint_state_publisher节点,发布机器人的关节状态  -->
        <node name="joint_state_publisher" pkg="joint_state_publisher" type="joint_state_publisher" ></node> 
    
        <!-- 运行robot_state_publisher节点,发布tf  -->
        <node name="robot_state_publisher" pkg="robot_state_publisher" type="robot_state_publisher"  output="screen" >
            <param name="publish_frequency" type="double" value="50.0" />
        </node>
    
        <!-- 在gazebo中加载机器人模型-->
        <node name="urdf_spawner" pkg="gazebo_ros" type="spawn_model" respawn="false" output="screen"
              args="-urdf -model mrobot -param robot_description"/> 
    
    </launch>
    

    启动方法:

    $ roslaunch mybot_gazebo view_mybot_gazebo_world.launch
    

    运行效果:
    在这里插入图片描述
    启动键盘控制节点:

    $ roslaunch mbot_teleop mbot_teleop.launch
    

    本文件来自课程代码包,不再贴出
    经测试,可以实现机器人模型在gazebo环境下的运动

    2.完成传感器仿真,在rviz中显示传感器数据

    • 摄像头仿真
    $ rqt_image_view
    

    显示效果:
    在这里插入图片描述

    • Kinect仿真
    $ rosrun rviz rviz
    

    添加robotmodel,pointcloud2,选择kinect_link作为基坐标系,在pointcloud2中选择深度信息显示,结果如下图所示
    在这里插入图片描述

    激光雷达仿真与上述方法类似


    结语

    本讲完成了xacro机器人模型的完善,并在gazebo仿真环境中完成了运动控制和传感器的仿真,并在rviz中显示了传感器的仿真效果。
    本讲中有以下知识点还需要深入理解:

    另外在本讲的练习中发现了一些问题:

    • 建立xacro模型并启动gazebo后,发现模型会发生x轴方向的滑动
      本人将模型中两个后轮的质量改为50后,有一定的改善,但未能根本解决该问题,另外修改摩擦系数几乎没有效果,初步判断为控制器controller属性没有修改正确。
    展开全文
  • 两个需求理论 对于产品经理,有两个非常经典的需求理论,就是“马斯洛需求层次理论”和“KANO模型”。 马斯洛需求层次理论就不用多说了,初高中的时候课本里就提到过。马斯洛把一个人的需求分为生理需求、安全...

    两个需求理论

    • 对于产品经理,有两个非常经典的需求理论,就是“马斯洛需求层次理论”和“KANO模型”。

    在这里插入图片描述

    • 马斯洛需求层次理论就不用多说了,初高中的时候课本里就提到过。马斯洛把一个人的需求分为生理需求、安全需求、社交需求、尊重需求和自我实现的需求,一般来说,这五个需求映射到产品上,则越是金字塔底部的需求,需求面越大,但ARPU可能越小,比如肚子不饿的生理需求,10块钱的炒粉就可以满足。

    在这里插入图片描述

    • KANO模型,是东京理工大学教授狩野纪昭(Noriaki Kano)发明的对用户需求分类和优先排序的有用工具,以分析用户需求对用户满意的影响为基础,体现了产品性能和用户满意之间的非线性关系。

    KANO模型把产品需求分成五类:

    1. 基础型需求
      基础性需求是一个产品最基础的功能,这个需求不能够被满足,则产品就不能正常使用,比如IM软件的打字聊天功能,美颜软件的拍摄功能等等。

    2. 期望型需求
      期望型需求的效果,是让用户满意度提升。比如微信聊天中的表情包,用户期望有一种俏皮的、不局限于文字的聊天方式,表情包恰好满足了用户这一点。

    3. 兴奋(魅力)型需求
      兴奋型需求也称为魅力型需求,是超出用户预期的,让用户满意度大幅提升的需求。比如微信的“拍一拍”和动态表情,用户并没有这个功能的预期,但微信做出来之后,大家也玩儿得不亦乐乎;还有当年的微信红包,用户突然发现微信中就可以直接发红包了,足不出户,就能完成拜年互动,于是,一个春节,借着红包的力量,微信支付一下子成为可以比肩支付宝的支付平台。
      例如:拍一拍和动态表情

    • 值得注意的是,这五种需求类型,并不是一成不变的,随着行业的进步,他们之间可能会发生转变。比如微信红包在刚推出时,属于魅力型需求,但随着红包成为趋势,用户也要求其他社交产品做红包功能,这就变成了期望型需求。
    1. 无差异型需求
      无差异性需求则是用户并不在意的需求,无论有或没有,用户体验都并不会产生较大的影响,用户满意度也并不会提高或降低。做无差异性需求看似是一个鸡肋的、无意义的行为,但在当下互联网产品大幅度内卷的情况下,几乎每个平台类APP都在疯狂堆砌这些功能,试图抢占用户时间——打车软件做团购、团购软件做打车、地图软件做购物、工具软件做内容,这大抵就是做产品的歧路罢(当然,实际上也有公司战略的考虑)。

    2. 反向型需求
      顾名思义,反向型需求就是做了之后,用户体验和用户满意度会下降的需求。在这里我忍不住要吐槽知乎,现在的知乎,充斥着各种小故事小短文,最关键的是,小故事读着正爽,来了一句“ 最低0.3元/天开通会员,查看完整内容 ”,掐死知乎的心思都有了。值得一说的是,现在的互联网产品中,反向型需求一般是因为商业诉求同用户体验产生了矛盾,做产品是为了盈利,追求效益无可厚非,而产品经理,就要在商业诉求和用户体验中反复横跳,做放纵与克制间的守夜人。

    马斯洛需求层次理论,说得是人性。KANO模型,对应着产品功能。

    展开全文
  • 先不管CAP是什么,就谈谈对于一个分布式的系统,它有哪些特征或行为。 (1)分布式系统会把微服务部署在多个节点上 (2)每个节点都有可能存储数据,一份数据可能在多个节点上有副本 (3)节点之间通过网络进行...
  • 跟大家谈一谈电商ROI理论及计算公式。 图片来源:123rf.com.cn 【支付收款】23年互联网上市公司,境内外牌照认证,优惠力度大,最快1秒到账点此查看>> ROI是什么?相信刚接触电商的外贸商家乍一听这个词...
  • 数据库 BASE理论

    2021-02-25 11:28:02
    可以理解为在一个平台中,假设部分出现故障,这个时候系统应该在允许这些故障出现的情况下依旧保持部分功能(应该是核心功能)可以正常使用,另一部分功能出现些许可以允许的问题。 响应时间上的损失:正常情况下的...
  • 什么是ZooKeeper-第347篇 ZooKeeper安装-第348篇 ZooKeeper数据结构和实操-第349篇 如果没有学过 ZooKeeper,那么本文将会是你进入 ZooKeeper 大门的垫脚砖;如果你已经接触过 ZooKeeper ,那么本文将带你回顾...
  • 系列导读本系列基于64位平台、1Page=8KB 今天我们开始拉开《Go语言轻松系列》第二章「内存与垃圾回收」的序幕。关于「内存与垃圾回收」章节,大体从如下三大部分展开:知识预备(为后...
  • 所以,从某种意义上讲,Java语言的devise者决定语言和编译代码将独立于平台,但由于代码最终必须在物理平台上运行,他们select将所有依赖于平台的代码JVM。这个对于JVM的要求与你的Turbo C例子相反。 使用Turbo C,...
  • 测试理论——SIT测试 和 UAT测试概念

    千次阅读 2020-12-21 21:57:33
    原标题:测试理论——SIT测试 和 UAT测试概念来源:http://www.51testing.comSIT测试和UAT测试在企业级软件的测试过程中,经常会划分为三个阶段——单元测试,SIT和UAT,如果开发人员足够,通常还会在SIT之前引入...
  • (一)基本概念 (1)短视频概念 短视频顾名思义即时长较短的视频,目前学术界尚未给出一个明确的定义。它是互联网内容传播的一...这些平台的短视频一般具有以下特点:一是视频长度短一般控制在30 秒以内,二是制作门
  • 软件测试基础知识 + 面试理论(超详细)

    千次阅读 多人点赞 2021-02-25 10:47:13
    三、软件测试工程师的工作内容四、常见的软件生命周期模型五、软件开发的几个阶段六、软件bug的五个要素七、软件测试的分类八、什么是测试用例九、测试用例几大要素【面试理论知识】1、你的测试职业发展是什么?...
  • C语言是什么

    千次阅读 2021-05-24 07:48:03
    尽管 C 语言提供了许多低级处理的功能,但仍然保持着跨平台的特性,以一个标准规格写出的 C 语言程序可在包括类似嵌入式处理器以及超级计算机等作业平台的许多计算机平台上进行编译。 简介 C 语言是一门面向过程的...
  • 企业级业务架构设计理论与方法

    千次阅读 2021-04-07 00:50:38
    一级流程可以理解为端到端流程,什么是端到端流程?从利益相关方这一端回到利益相关方。有的企业不叫利益相关方,叫价值共同体,其本质是一样,只是叫法不同。在流程规划时,企业应重点关注四类利益相关方:外部顾客...
  • 什么是去中心化交易平台

    千次阅读 2021-10-12 17:41:21
    伴随着 DeFi 快速崛起的大背景下,其中的去中心化交易平台 DEX 也呈现强劲的增长势头。特别是在引入自动做市商模式 AMM 之后,DEX 有效解决了长久以来存在的流动性不足的问题。虽然,现在的 DEX 还处在发展的初期,...
  • 一、什么是矿池呢? 通过矿池挖到比特币的概率相对更大,矿池挖矿的过程中会产生比特币奖励,奖励的比特币会按照矿工贡献算力占比对其进行合理的分配,现在越来越多的用户开始使用矿池挖矿,比单独挖矿获得的收益更多...
  • 针对上述挑战,北京大学软件与微电子学院基于头歌打造了全新软件工程线上课程《软件工程理论与实践 —— 思政与实战相结合的能力训练》,提供了100多个配套实战案例,面向全国高校软件工程系列课程教师和同学开放!
  • 来源:人机认知与实验室摘要:移动机器人的人机交互正处于早期阶段。大部分与机器人相关的用户交互都被数控能力所限制,向用户提供的最普遍的界面是以自动化平台提供的视频和对机器人的一定程度上的路径...
  • CMake的理论与实践

    千次阅读 2021-11-05 15:12:55
    1、什么是CMake CMake 是"Cross platform MAke"的缩写 • 一个开源的跨平台自动化建构系统,用来管理程序构建,不相依于特定编译器 • 需要编写CMakeList.txt 文件来定制整个编译流程(需要一定学习时间) • ...
  • 清华元宇宙报告 理论框架

    千次阅读 2021-11-23 22:37:46
    本期的智能内参,我们推荐清华大学新媒体研究中心的报告《2020-2021年 元宇宙发展研究报告》, 从元宇宙的理念框架、技术底座、产业现状和指数体系等方面解答元宇宙到底是什么。如果想收藏本文的报告,可以.
  • 视觉SLAM理论与实践学习笔记(一)

    千次阅读 2021-01-15 18:34:26
    视觉SLAM理论与实践学习笔记(一)熟悉linuxSLAM综述文献阅读Cmake练习理解ORB-SLAM2框架使用摄像头或视频运行ORB-SLAM2 熟悉linux SLAM综述文献阅读 Cmake练习 理解ORB-SLAM2框架 使用摄像头或视频运行ORB-SLAM...
  • 武汉理工理论考试pta判断题题库

    千次阅读 2021-05-19 19:48:22
    《武汉理工理论考试pta判断题题库》由会员分享,可在线阅读,更多相关《武汉理工理论考试pta判断题题库(14页珍藏版)》请在人人文库网上搜索。1、,判断题题库,.,1.函数的形参都属于局部变量。,2.函数的实参只能是常量...
  • java是一种什么语言?

    2021-02-12 10:38:56
    java是什么语言?Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,...
  • 在介绍java前端后端之前,大家需要简单了解什么是Java?下面我们就给大家详细介绍Java以及Java前端后端的概念,希望对需要的朋友有所帮助!Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里...
  • 未能找到指定主机服务器是什么意思 内容精选换一换b=bit;字符B=Byte;字节1Byte=8bit宽带网速如10M网络是指10Mb/s=10Mbit/s字符每秒这是指带宽实际理论最高网速为4*1024/8KByte/s=512KB/s字节每秒,这是指网速。也...
  • 计算机系统软件的核心是什么

    千次阅读 2021-06-25 01:36:41
    系统软件的核心应该应该操作系统,困为计算机软件都是要以操作系统为平台的。软件系统(Software Systems)是指由系统软件、支撑软件和应用软件组成的计算机软件系统,它是计算机系统中由软件组成的部分。它包括操作...
  • 王珊珊 冉伦 多阶段医疗系统下病人滞留分析与应用 作者:吴晓丹 许荣荣 马秋月 CHU Chao-Hsien 关系质量、信息分享与企业间合作行为:IT能力的调节作用 作者:林舒进 庄贵军 黄缘缘 在线旅游平台混合不透明营销策略...
  • 小米大数据平台OLAP架构 演进 一、数据仓库 1、离线数据仓库的架构 数据仓库一般架构 分析型系统进行联机数据分析,一般的数据来源是数据仓库,而数据仓库的数据来源为可操作型系统,可操作型 系统的数据来源...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 279,710
精华内容 111,884
关键字:

平台理论是什么