everything is a message

我们在scratch3_adapter的项目介绍中说

我们不想针对某个硬件产品发布一个客户端,我们相信创意来自广泛的连接,我们致力于做一个中立的东西,将Scratch3.0连接到更广阔的领域,我们想做到宽围墙

事实上,scratch3_adapter比我们所描述的还有更中立一些。它中立起来,连自己的名字(scratch3_adapter)都忘了,scratch3_adapter不止可以将Scratch3.0接入各种有趣的硬件和AI中(如它的名字所暗示的),也可以连接Blockly和各种有趣的硬件/AI

在这篇文章中,我们将介绍如何使用scratch3_adapter连接blockly与micro:bit。利用文章演示的方法,你可以将blockly接入到其他有趣的系统中,远不止是micro:bit

也许在此我们应该叫它:blockly_adapter :)

初心

模仿是最大的致敬

构建scratch3_adapter之前的一段时间,我有两个周末沉迷于《ROS机器人编程实践》,为这套系统的架构所折服,看到作者设计得正交且清晰的API时,叹服之余,也暗自坐在书店地上设想: API的背后是怎样的?如果我来做,我会怎么实现它?而后带着自己的设想,和作者源码做对照。

看这本书之前我正巧看完Pieter Hintjens的《ZeroMQ:云时代极速消息通信库》,zeromq是个极其灵活、强大的工具,健壮有力而优美。有了zeromq这把称心的斧头,那段时间里我倾向于把一切都视为钉子(everything is a message),于是我决定把整个ROS视为一个消息系统。

开始重造轮子时,从sub和pub入手,觉得无碍; 到了重构rescore命令行工具时,我将其视为一个服务发现组件,挑选工具时,在python原生的dict和redis中犹豫,最终决定选择redis。

后来在翻阅ROS的资料时,发现作者也正在重构ROS(ROS2),他说ROS本质上时一个匿名的消息系统,早年他在构建ROS时,由于社区里工具的匮乏,他造了很多轮子,如果ROS写于今天,他会选择zeromq和redis(为了实时性,他最终选了DDS)。看到这儿我颇为振奋,觉得方向猜的还挺准

我把早先的尝试放在ROLS(Robot Operating Lite System),往后折腾的时候发现,重造一个ROS比我想象的要复杂得多。那段时间,我正好在考虑为Scratch3.0写一个插件系统,发现可以模仿ROS的局部设计来做这个插件系统。于是决定先放下ROLS,在插件系统这个更有实际价值的场景里,去实践从ROS学来的东西

ROS本质上是一个匿名的消息系统

ROS作者阐述的ROS消息的匿名性以及由此带来的回报,令我着迷。以至于我一直试图将scratch3_adapter设计为一个匿名的消息系统,就像你刚看完一本精彩的小说,恨不得说话的语气都模仿喜欢的角色。在设计scratch3_adapter时,我有一天跟@izuo说,也许我们不该叫它scratch3_adapter,因为Scratch3.0仅仅是它的一个client,并不占主体地位,scratch3_adapter可以对接到blockly或者任何其他的地方,没有任何差异。

但我后来意识到(在实际使用extension时),匿名性对我们价值并不大,在scratch3_adapter的使用场景里,各个节点并不是对等的,它其实不是一个分布式的系统(ROS本质却是一个分布式的系统)。scratch3_adapter有它的中心,积木化界面就是它的中心。于是我们着手开始削弱匿名性,如果你看到代码的变迁过程,就会看到想法的变迁过程

尽管我们已经削弱了它的匿名性,也继续沿用scratch3_adapter这个名字,但它依然有很强的适应性,本质的原因我想源自它是一个消息系统。

在使用scratch3_adapter的时候,你可以很轻松将scratch3替换为blockly,使用scratch3_adapter连接你的blockly和其他系统(硬件或AI),而几乎不会有任何损失。

why blockly

谈论怎么做之前,我们先来谈谈为何要这么做。

那么,我们为何要连接blockly和硬件/AI?

我认为最重要的一个原因是,blockly是少儿编程领域,大多数教学平台的基础。而教学平台除了教学软件相关的课程,也希望教学硬件和AI。

ps:值得一提的是,scratch-blocks本质上是blockly,而不是Scratch3.0,关于这一点,常常会引起一些混乱

如果你问blockly为何是少儿编程领域内,大多数教学平台的基础?

这个问题会逼着我们思考讨论的特性和差异,我之前做过这个讨论: Blockly与Scratch3.0的比较分析及选型建议

尽管原因很多(如我前边谈的),但重要的原因,简而言之,是因为blockly和Scratch3.0的定位不同,Scratch3.0定位为playground,而blockly只是个轻巧的编辑器。所以blockly可以承载更多个性化的定制部分,教育很难千篇一律,大家倾向一个轻巧灵活的工具以适应课程。

不过,目前的少儿编程教育蛮千篇一律的 :)

你会看到少儿编程这个领域,目前似乎只有两种模式:

  • 一种是playground式的,国外如scratch,国内如编程猫
    • 我认为playground的价值在于"低门槛、高天花板、宽围墙",而开放和社区是核心,所以我正在写一篇文章:《为何你应该选择Scratch3.0作为playground》
  • 一种是关卡式的,如code.org、Tynker
    • 关卡式的平台其实很套路,架构一个这样的平台基本可以分三步走完成,这部分我们之后有机会再细说

scratch3_adapter是个中立的系统,以上两种模式的平台我们都支持

连接blockly与micro:bit

目标

构建一个blockly积木块,用来在micro:bit上打印任意字符串

动起手来

下边跟着我一起动起手来,脖子扭扭屁股扭扭

步骤1: 编写blockly源码

在此假设你对blockly开发有基本的了解,如果你blockly开发并不了解,可以看我的系列教程:blockly入门与介绍 (教程链接在文末)

我们先直接上源码,稍后讲解:blockly_2.2_custom_block_run_js_with_interpreter

除去html和无关紧要的部分,将scratch3_adapter接入blockly的核心源码就几行:

首先是建立连接:

1
var socket = io('ws://127.0.0.1:12358' + '/test', {transports: ['websocket']});

这一行代码,将blockly课程或创作平台与scratch3_adapter进行连接。

ps: scratch3_adapter用到好几个端口,12358是其中一个,scratch3_adapter中端口的选择藏着彩蛋 :) 有兴趣的小伙伴可以自己去解读

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
      var wrapper = function(text) {
        console.log(text.data);
        return socket.emit('actuator',
                         {id: 'microbit',
                          data: text.data,
                          topic: "actuators/say"});
      };
      
      interpreter.setProperty(scope, 'say',
        interpreter.createNativeFunction(wrapper));

这是我们使用blockly积木控制micro:bit(硬件)的关键所在,如果你是个有经验的程序员,我已不必多说什么了,你已经可以使用scratch3_adapter将blockly接入任何硬件/AI

enjoy it ~

步骤2: 打开scratch3_adapter,选择内置的micro:bit插件

此刻你应该看到micro:bit背后的信号灯在高频闪烁,如果它没有高频闪烁,则按一下micro:bit背部的复位按钮

步骤3: 运行!

点击运行blockly积木块,你将看到blockly已经能成功驱动micro:bit了

你可以把blockly接入更多的硬件,包括控制你的蓝牙硬件、你的ROS机器人、你那跑着opencv的树莓派或者你童年那辆心爱的玩具四驱车,可以参考我们的:开发手册

想象空间

我们把scratch3_adapter设计为一个独立的小工具(10M左右),而不是连同Scratch3.0一起用electron打包在一起,个中原因除了

人生苦短、我用Python

更重要的是,我们想让它成为一个通用的工具,无论你的网站是什么,无论你使用scratch3.0、blockly还是某种自主研发的其他什么东西,你都无需做什么伤筋动骨的事,只需要在网站中写一些js代码,你就能将网站接入scratch3_adapter,进而与各类开源硬件/AI相连。

这部分值得玩味的地方是,你可以在网页上做所有的教学,无论是结合某一款开源硬件还是某一个AI系统。而scratch3_adapter将允许你用积木来驱动它们,你也能拿到积木/代码在外部系统的执行结果,判断学生是否掌握该知识点,而这对教学可能是很重要的。

scratch3_adapter在向ROS学习,我们之后将引入action,如此一来你甚至可以设置开放性的课程,诸如某个关卡是要求学生用积木写一个智能体,让它走出现实世界的迷宫,里边可能涉及到各种复杂的策略和不可预期的情况,这种系统必须是异步的,最好带有状态机,ROS在这块做得十分惊艳。ROS的的action适合这种任务。尽管scratch3_adapter目前也能支持,但可能需要你写比较多的代码。对智能的关注是我的兴趣之一,我之后会来增强这块。