机器人、迷宫和包容体系结构

用 Java 语言编写虚拟机器人

正如 IBM 计算机程序员 Paul Reiners 在本文中指出的,机器人模拟器既是严肃的研究工具,也是能够通过 Java™ 编程创造乐趣的领域。学习如何使用 Simbad(一种基于 Java 3D 技术的开放源码机器人模拟器)和 Java 语言创建能够跟踪光源和走迷宫的虚拟机器人,并了解包容体系结构的机器人设计概念。

Paul D. Reiners, 计算机程序员, EMC

Paul ReinersPaul Reiners 是一位 Sun 认证的程序员和开发员。他是多个开源程序的开发人员,包括 Automatous Monk、Twisted Life 和 Leipzig。Reiners 从伊利诺斯大学 Urbana-Champaig 分校获得应用数学(计算理论)硕士学位。他目前居住在明尼苏达州,在业余时间喜欢演奏电子贝斯,带着他的宠物鼠 Fred 和 Mortimer 闲逛,以及参加 TopCoder 的比赛。



2008 年 1 月 11 日

简介

机器人学很早就超出了科学幻想的领域,并在工业自动化、医疗、太空探索等领域发挥着重要作用。软件机器人模拟器不但简化了机器人工程师的开发工作,还为研究人工智能(artificial intelligence,AI)算法和机器学习提供了工具。以这种研究为中心的模拟器之一是开放源码的 Simbad 项目,它基于 Java 3D 技术(参见 参考资料)。本文讲解如何使用 Simbad 工具箱编写虚拟机器人,并介绍包容体系结构(subsumption architecture) 的机器人设计原理。

本文首先简要概述机器人学并解释包容体系结构概念。然后介绍 Simbad 工具箱并演示如何在 Simbad 中实现包容体系结构。然后使用这个体系结构编写一个简单的机器人。最后,讨论迷宫并编写第二个机器人,与 Homer Simpson 不同(参见 参考资料),这个机器人会自己走出迷宫。这里的机器人不是现实存在的,但是 “活” 在 Simbad 的虚拟世界中。


机器人编程

机器人 这个词没有被普遍接受的定义。根据本文的目的,可以认为一个机器人由三个部分组成:

  • 一个传感器(sensor) 集合
  • 一个定义机器人的行为的程序
  • 一个传动器(actuator)受动器(effector) 集合

传统的机器人学

在传统的机器人学中(也就是 1986 年之前的机器人学),机器人拥有一个中央 “大脑”,这个大脑构建并维护环境的 “地图”,然后根据地图制定计划。首先,机器人的传感器(例如接触传感器、光线传感器和超声波传感器)从它的环境中获得信息。机器人的大脑将传感器收集的所有信息组合 起来并更新它的环境地图。然后,机器人决定运动的路线。它通过传动器和受动器执行动作。传动器基本上是一些发动机,它们连接到受动器,受动器与机器人的环境交互。受动器包括轮子和机械臂等(传动器 这个词常常用来泛指传动器或受动器)。

简单地说,传统的机器人接收来自传感器(可能有多个传感器)的输入,组合传感器信息,更新环境地图,根据它当前掌握的环境视图制定计划,最后执行动作。但是,这种方法是有问题的。问题之一是它需要进行大量计算。另外,因为外部环境总是在变化,所以很难让环境地图符合最新情况。一些生物(比如昆虫)不掌握外部世界的地图,甚至没有记忆,但是它们却活得非常自在;模仿它们会不会更好呢?这些问题引出了一种新型的机器人学,称为基于行为的机器人学(behavior-based robotics,BBR)。BBR 在当今的机器人实验室中占主要地位。

包容体系结构

可以使用包容体系结构(subsumption architecture)实现 BBR。包容体系结构的发明者是 Rodney A. Brooks(MIT AI Lab 的现任领导)在 1986 年的文章 “Elephants Don't Play Chess” 中提出了包容体系结构(参见 参考资料)。基于行为的机器人依赖于一组独立的简单的行为。行为的定义包括触发它们的条件(常常是一个传感器读数)和采取的动作(常常涉及一个受动器)。一个行为建立在其他行为之上。当两个行为发生冲突时,一个中央仲裁器(arbitrator) 决定哪个行为应该优先。机器人的总体行为是突然的(emergent),根据 BBR 支持者的说法,它的效果好于其部分之和。较高层行为包容(subsume) 较低层行为。我们并不创建整个机器人,只需添加行为并看看会发生什么。


Simbad:一个机器人模拟环境

LEGO Mindstorms

本文主要关注软件机器人的构建,但是如果希望构建物理机器人,那么可以考虑使用 LEGO Mindstorms。

LEGO Mindstorms 总部的宣言说,“我们将让机器人学发生巨变,就像 iPod 给音乐界带来的影响。” LEGO 于 1998 年推出了 Mindstorms 机器人学工具箱的第一版。这个工具箱很快就变得畅销了,大大超出了 LEGO 的预期。尽管它的价格似乎不便宜($250),但是要知道这只相当于 iPod Classic 的价格,而且每个人 都拥有 iPod。

但是,iPod 没 Mindstorms 那么容易破解。在 Mindstorms 发布后不久,硬件黑客就开始取出 Mindstorms RCX brick(Mindstorms 机器人的 “大脑”)并进行反向工程。LEGO 没有预料到这种情况,而且不确定是否应该制止这种做法。最后,LEGO 的管理层决定允许 Mindstorms 黑客的这种行为。

由此产生了一个兴旺的 Mindstorms 社区(参见 参考资料)。尽管 Mindstorms 只附带一种拖放式图形化编程语言 NXT-G,但是软件黑客很快就在 Mindstorms 中添加了对 C 和 Java 等其他语言的支持。结果,大约一半的 Mindstorms 由成年人使用。

可以使用 Simbad 在软件中模拟机器人。该项目 Web 站点指出,它 “让程序员能够编写自己的机器人控制器、修改环境和使用可用的传感器。它主要向研究人员/程序员提供一个基本工具,用来在自治机器人学和自治代理上下文中研究情景人工智能(Situated Artificial Intelligence)、机器学习和其他 AI 算法。”

Simbad 是由 Louis Hugues 和 Nicolas Bredeche 用 Java 语言编写的。在符合 GNU General Public License 的条件下,可以免费使用和修改这个项目(可从 SourceForge.net 上获得)。

技术细节

Simbad 环境可以包含代理(Agent,也就是机器人)和固定对象(盒子、墙、灯等等)。Simbad 环境中的时间划分成离散的 “嘀嗒(tick)”。Simbad 在代理之间调度时间分配。与物理机器人一样,Simbad 代理也有传感器(距离、接触、光线等等)和传动器(常常是轮子)。在每个嘀嗒时刻,一个机器人可以执行动作。

代理通过覆盖 performBehavior() 方法决定它们的行为。在 performBehavior() 中,机器人可以读取传感器读数并设置它的平移和旋转速度。performBehavior() 执行的时间很短,所以不能发出 “前进一米” 这样的命令。为了解决这个限制,通常必须跟踪机器人的状态。还可以使用计时器变量记录保持当前状态的时钟嘀嗒数。

Simbad API

在本文的练习中,主要使用两个 Simbad API 包:

  • simbad.sim:这个包中的类代表机器人和它所在的环境。包括:
    • AgentAgent 就是机器人。
    • Arch:机器人可以绕过或从下面通过的拱形结构。
    • Box:可以作为机器人的环境中的障碍物。
    • CameraSensor:可以从机器人的视角查看机器人的环境。
    • EnvironmentDescription:代表 “环境”,可以在其中添加机器人和墙或盒子等对象。
    • LampActuator:可以添加到机器人上的灯。
    • LightSensor:感应光线的强度。
    • RangeSensorBelt:包含机器人周围的一组距离传感器。
    • RobotFactory:用这个类在机器人上添加传感器。
    • Wall:另一种障碍物。
  • simbad.gui:这个包中的类显示机器人环境并提供控制手段。这些类包括:
    • Simbad:显示机器人环境、传感器输入和控制的框架。

在 Simbad 中实现包容体系结构

Roomba

Roomba 用真空吸尘器打扫地毯。它是由 iRobot 开发的,这是一家由三位 MIT 校友(Rodney Brooks、Colin Angle 和 Helen Greiner)创办的公司。Roomba 是使用包容体系结构构建的并提供一个开放接口,可以通过这个接口实现各种有趣的效果。Tod E. Kurt 的书 Hacking Roomba 介绍了许多有意思的应用(参见 参考资料)。

要在 Simbad 中实现包容体系结构,首先定义 Agent 的一个子类 BehaviorBasedAgentBehaviorBasedAgent 包含一个 Behavior 数组和一个 boolean 矩阵,这个矩阵决定 Behavior 的 优先级:

private Behavior[] behaviors;
private boolean suppresses[][];

BehaviorBasedAgent 作为 Behavior 的调度器。清单 1 中的代码循环处理各个行为(使用 currentBehaviorIndex 类变量记录接下来应该执行哪个行为)并在它们之间做出仲裁:

清单 1. 循环处理行为并进行仲裁的代码
protected void performBehavior() {
   boolean isActive[] = new boolean[behaviors.length];
   for (int i = 0; i < isActive.length; i++) {
      isActive[i] = behaviors[i].isActive();
   }
   boolean ranABehavior = false;
   while (!ranABehavior) {
      boolean runCurrentBehavior = isActive[currentBehaviorIndex];
      if (runCurrentBehavior) {
         for (int i = 0; i < suppresses.length; i++) {
            if (isActive[i] && suppresses[i][currentBehaviorIndex]) {
               runCurrentBehavior = false;

               break;
            }
         }
      }

      if (runCurrentBehavior) {
         if (currentBehaviorIndex < behaviors.length) {
            Velocities newVelocities = behaviors[currentBehaviorIndex].act();
            this.setTranslationalVelocity(newVelocities
                  .getTranslationalVelocity());
            this
                  .setRotationalVelocity(newVelocities
                        .getRotationalVelocity());
         }
         ranABehavior = true;
      }

      if (behaviors.length > 0) {
         currentBehaviorIndex = (currentBehaviorIndex + 1)
               % behaviors.length;
      }
   }
}

注意,performBehavior() 覆盖了 simbad.sim.Agent.performBehavior()

Behavior 有两个 abstract 方法:

  • isActive() 返回一个布尔值,表示根据当前的传感器状态,是否应该激活这个行为(所有 Behavior 共享一组传感器)。
  • act() 返回两个速度(依次为平移速度和旋转速度),表示希望发动机执行的动作。

示例:追逐光线的漫游机器人

现在创建一个机器人示例,它有以下四个 Behavior(按照优先级的降序列出),见清单 2 到清单 5(下载 本文使用的源代码) 。

  • Avoidance:在发生冲突之后或为了避免冲突,改变方向。
  • LightSeeking:向光线移动。
  • Wandering:随机地改变方向。
  • StraightLine:沿直线移动。
清单 2. Avoidance 类(基于 Simbad SingleAvoiderDemo.java 演示代码)
public boolean isActive() {
   return getSensors().getBumpers().oneHasHit()
         || getSensors().getSonars().oneHasHit();
}

public Velocities act() {
   double translationalVelocity = 0.8;
   double rotationalVelocity = 0;
   RangeSensorBelt sonars = getSensors().getSonars();
   double rotationalVelocityFactor = Math.PI / 32;
   if (getSensors().getBumpers().oneHasHit()) {
      // if ran into something
      translationalVelocity = -0.1;
      rotationalVelocity = Math.PI / 8
            - (rotationalVelocityFactor * Math.random());
   } else if (sonars.oneHasHit()) {
      // reads the three front quadrants
      double left = sonars.getFrontLeftQuadrantMeasurement();
      double right = sonars.getFrontRightQuadrantMeasurement();
      double front = sonars.getFrontQuadrantMeasurement();
      // if obstacle near
      if ((front < 0.7) || (left < 0.7) || (right < 0.7)) {
         double maxRotationalVelocity = Math.PI / 4;
         if (left < right)
            rotationalVelocity = -maxRotationalVelocity
                  - (rotationalVelocityFactor * Math.random());
         else
            rotationalVelocity = maxRotationalVelocity
                  - (rotationalVelocityFactor * Math.random());
         translationalVelocity = 0;
      } else {
         rotationalVelocity = 0;
         translationalVelocity = 0.6;
      }
   }

   return new Velocities(translationalVelocity, rotationalVelocity);
}
清单 3. LightSeeking 类(基于 Simbad LightSearchDemo.java 演示代码)
public boolean isActive() {
   float llum = getSensors().getLightSensorLeft().getAverageLuminance();
   float rlum = getSensors().getLightSensorRight().getAverageLuminance();
   double luminance = (llum + rlum) / 2.0;

   // Seek light if it's near.
   return luminance > LUMINANCE_SEEKING_MIN;
}

public Velocities act() {
   // turn towards light
   float llum = getSensors().getLightSensorLeft().getAverageLuminance();
   float rlum = getSensors().getLightSensorRight().getAverageLuminance();
   double translationalVelocity = 0.5 / (llum + rlum);
   double rotationalVelocity = (llum - rlum) * Math.PI / 4;

   return new Velocities(translationalVelocity, rotationalVelocity);
}
清单 4. Wandering
public boolean isActive() {
   return random.nextDouble() < WANDERING_PROBABILITY;
}

public Velocities act() {
   return new Velocities(0.8, random.nextDouble() * 2 * Math.PI);
}
清单 5. StraightLine
public boolean isActive() {
   return true;
}

public Velocities act() {
   return new Velocities(0.8, 0.0);
}

清单 6 指定行为的优先次序:

清单 6. 指定行为的优先次序
private void initBehaviorBasedAgent(BehaviorBasedAgent behaviorBasedAgent) {
   Sensors sensors = behaviorBasedAgent.getSensors();
   Behavior[] behaviors = { new Avoidance(sensors),
         new LightSeeking(sensors), new Wandering(sensors),
         new StraightLine(sensors), };
   boolean subsumes[][] = { { false, true, true, true },
         { false, false, true, true }, { false, false, false, true },
         { false, false, false, false } };
   behaviorBasedAgent.initBehaviors(behaviors, subsumes);
}

注意,尽管这个示例中的 Behavior 具有总体优先次序,但是不一定要这样做。

作为练习,您可以试试以下改进:

  • 添加一个社会化行为:向朋友移动,而远离敌人。
  • 添加一个避光行为。
  • 在一些机器人上添加灯,让它们能够相互吸引。

迷宫

“有了!我们可以用 Tremaux 的算法走出这个迷宫!” — Lisa Simpson

在现有的几种解迷宫算法中,有两种常用算法使用固定数量的内存,而与迷宫的大小无关。这两种算法是沿墙走(wall-following)Pledge 算法 (由英国埃克塞特的 Jon Pledge 在 12 岁时发明)。Tremaux 的算法(Lisa Simpson 选择的算法)是一种出色的算法,但是为了简单,本文主要关注 “沿墙走” 和 Pledge 算法。

迷宫生成算法

不但有许多解迷宫算法,还有许多用来生成迷宫的算法。本文所考虑的迷宫被称为完美迷宫(perfect mazes)。在完美迷宫中,从任何一点到另一点都有且只有一条路径(这条规则排除了那些包含环路、“孤岛” 或封闭部分的迷宫)。大多数完美迷宫生成算法首先生成一个只有外墙的迷宫,然后让墙向内一段段地 “生长”。为了确保迷宫是完美的,每当添加一段新墙时,需要确保不会造成环路或封闭部分。

沿墙走

沿墙走是一种简单的解迷宫算法,您可能在小时候就学过了。在用这种算法解迷宫时,只需要把左手一直放在左边的墙上(或者把右手一直放在右边的墙上),然后沿着墙走,直到走出迷宫。很容易看出,如果迷宫的边界上有一个入口和一个出口,那么这种算法总是有效的。但是,如果目标在一个孤岛(即迷宫中不与其他部分相连的部分)中,那么这种算法无法找到解决方案,因为它无法 “跳” 到孤岛上。

Pledge 算法

Pledge 算法比沿墙走算法复杂,能够解决更多的迷宫类型,因为它可以在孤岛之间跳跃。Pledge 算法的基本思想是,先选择一个绝对方向(比如东、南、西或北),然后总是尽可能朝这个方向走。我把这个方向称为偏好方向(favored direction)。当遇到墙时,向右转身并采用左手沿墙走方式,直到面向偏好方向而且 转身数的总和为零(顺时针转身是负值,逆时针转身是正值)为止。此时,继续沿偏好方向向前走。转身数的总和必须是零,这是为了避免陷入某些环路,比如大写字母 G 形状的环路(在纸上试着走一下这个形状,就会明白我的意思)。


Algernon:一个解迷宫机器人

现在该让您的朋友大吃一惊了,我们来构建一个解迷宫机器人,它的名字是 Algernon。

设计机器人

为了实现沿墙走或 Pledge 算法,需要知道机器人什么时候遇到交叉路口,以及在遇到交叉路口时应该走哪个方向。

有多种实现这一目标的方法,但是我们采用的方法是在机器人的左边安装一个声纳传感器。当左边出现通道时,这个传感器会发出警报。为了了解机器人正在走的通道什么时候到头了(即,机器人遇到了墙),在机器人的前面添加一个接触传感器。

实现沿墙走算法

我们使用 algernon.subsumption 包编写 Algernon(下载 源代码)。Algernon 是一个非常简单的机器人,可以按照简单的 “过程式” 方式编写它。但是,即使对于这么简单的机器人,使用包容编程也会使代码更加清晰,更模块化,更容易理解。

为了简化算法实现,假设墙的转弯都是直角的。所以,机器人的所有转身都是 90 度左转或 90 度右转。

如果分析一下(左手)沿墙走算法,就会发现它可以分解成四个行为:

  • 向前直走。
  • 当遇到墙时,向右转。
  • 如果看到左边有通道,就向左转。
  • 当到达目标时,停止。

现在需要决定这四个行为的优先次序。前面的列表正好是按照优先级的降序排列的。我们将编写四个类,它们都扩展 Behavior

  • GoStraight
  • TurnRight
  • TurnLeft
  • ReachGoal

清单 7 是 GoStraight 的代码。TRANSLATIONAL_VELOCITY 是一个设置为 0.4 的常量:

清单 7. 向前直走的行为代码
public boolean isActive() {
   return true;
}
  
public Velocities act() {
   double rotationalVelocity = 0.0;

   return new Velocities(TRANSLATIONAL_VELOCITY, rotationalVelocity);
}

清单 8 是 TurnRight 的代码。getRotationCount() 返回的值是以指定的旋转速度旋转 90 度所需的时钟嘀嗒数。

清单 8. 向右转的行为代码
public boolean isActive() {
   if (turningRightCount > 0) {
      return true;
   }

   RangeSensorBelt bumpers = getSensors().getBumpers();
   // Check the front bumper.
   if (bumpers.hasHit(0)) {
      backingUpCount = 10;
      turningRightCount = getRotationCount();

      return true;
   } else {
      return false;
   }
}
        
public Velocities act() {
   if (backingUpCount > 0) {
      // We back up a bit (we just ran into a wall) before turning right.
      backingUpCount--;

      return new Velocities(-TRANSLATIONAL_VELOCITY, 0.0);
   } else {
      turningRightCount--;

      return new Velocities(0.0, -Math.PI / 2);
   }
}

当 Algernon 向左转时,它先向前走一点儿,让它的背部超过左边通道的墙。然后,再向左转。最后,它需要再前进一点儿,让墙再次出现在它左边。清单 9 是 TurnLeft 的代码:

清单 9. 向左转的行为代码
public boolean isActive() {
   if (postGoingForwardCount > 0) {
      return true;
   }

   RangeSensorBelt sonars = getSensors().getSonars();
   // Check the sonar on the left.
   if (sonars.getMeasurement(1) > 1.0) {
      // There is a passageway to the left.
      preGoingForwardCount = 20;
      postGoingForwardCount = 40;
      turnLeftCount = getRotationCount();

      return true;
   } else {
      return false;
   }
}
        
public Velocities act() {
   if (preGoingForwardCount > 0) {
      preGoingForwardCount--;

      return new Velocities(TRANSLATIONAL_VELOCITY, 0.0);
   } else if (turnLeftCount > 0) {
      turnLeftCount--;

      return new Velocities(0.0, Math.PI / 2);
   } else {
      postGoingForwardCount--;

      return new Velocities(TRANSLATIONAL_VELOCITY, 0.0);
   }
}

清单 10 是 ReachGoal 的代码:

清单 10. 到达目标的行为代码
public boolean isActive() {
   RangeSensorBelt sonars = getSensors().getSonars();

   // Is there open space all around us? That is, are we out of the maze?
   double clearDistance = 1.2;
   return sonars.getMeasurement(0) > clearDistance
         && sonars.getMeasurement(1) > clearDistance
         && sonars.getMeasurement(3) > clearDistance
         && sonars.getMeasurement(2) > clearDistance;
}

public Velocities act() {
   // Stop
   return new Velocities(0.0, 0.0);
}

清单 11 是 Algernon 的主行为代码:

清单 11. Algernon 的行为控制代码
private void initBehaviorBasedAgent(
      algernon.subsumption.BehaviorBasedAgent behaviorBasedAgent) {
   algernon.subsumption.Sensors sensors = behaviorBasedAgent.getSensors();
   algernon.subsumption.Behavior[] behaviors = { new ReachGoal(sensors),
         new TurnLeft(sensors), new TurnRight(sensors),
         new GoStraightAlways(sensors) };
   boolean subsumes[][] = { { false, true, true, true },
         { false, false, true, true }, { false, false, false, true },
         { false, false, false, false } };
   behaviorBasedAgent.initBehaviors(behaviors, subsumes);
}

图 1 显示 Algernon 正在走一个迷宫:

图 1. Algernon 正在走迷宫
Algernon 正在走迷宫

注意,尽管这个机器人根本不了解关于迷宫(甚至墙)的任何信息,但是它能够走出迷宫。Algernon 没有计算迷宫出路的中央大脑。这就是包容体系结构能够取得的效果:用一组简单的层次化的行为构成复杂的看似有目的的行为。


结束语

在本文中,我们开发了虚拟机器人。开发真实的物理机器人要困难得。物理世界有各种各样的麻烦。例如,很容易让我们的沿墙走机器人平行于左边的墙移动。但是,在真实世界中墙的表面不是绝对平的,如何避免机器人靠向墙壁或离墙太远本身就是个难题。即使您喜欢编程,也不一定会喜欢开发真实的机器人。开发有趣的机器人不但需要编程技能,还需要机械技能。

如果您有兴趣构建自己的机器人,那么可以考虑使用 LEGO Mindstorms。另一种成本比较低的方法是构建 Biological Electronic Aesthetics Mechanics(BEAM)机器人。BEAM 进一步发展了基于行为的机器人思想,并完全避免了编程。通过硬连线构成总体行为,模拟反射响应行为。只需 $30 或更少的成本,再参考一些书,比如 Gareth Branwyn 的 Absolute Beginner's Guide to Building Robots(参见 参考资料),就可以用 BEAM 工具箱构建出您的第一个机器人。还可以购买并使用 Roomba。

在编写机器人并查看其他人的机器人代码之后,我很快就发现了一个令人吃惊的情况:只需很少的代码,就能够让机器人做许多事情(但是,为了让这些代码精确地实现目标,可能需要做许多分析和实验)。使用 LEGO Mindstorms 工具箱,只需半天时间,就能够构建一个简单的机器人。

对机器人学感兴趣的爱好者可以通过许多渠道提高技能,比如机器人图书、机器人竞赛、机器人视频资料和当地的机器人俱乐部。

参考资料

学习

  • 您可以参阅本文在 developerWorks 全球站点上的 英文原文
  • Simbad:访问 Simbad 机器人模拟器的项目网站。
  • Absolute Beginner's Guide to Building Robots(Gareth Branwyn,Que,2003 年):这是个非常适合初学者的机器人构建概述。
  • Mobile Robots(Joseph L. Jones 和 Anita M. Flynn,A K Peters, Ltd.,1998 年):本书讲解如何设计和构建廉价的小型机器人,清楚地解释了包容体系结构。本书在思想和概念方面尤其精彩。Flynn 和 Jones 都是 MIT AI Lab 的 Rodney Brooks 的学生。
  • 开源机器人技术工具包”(M. Tim Jones,developerWorks,2006 年 9 月):提供关于 Simbad 和其他机器人算法测试软件的信息。
  • Introduction to robotics technology”(Darrick Addison,developerWorks,2001 年 9 月):对机器人学和开放源码的机器人控制软件进行一般性介绍。
  • Flesh and Machines(Rodney A. Brooks,Pantheon Books,2002 年):在这本非常有趣的非技术性著作中,Brooks 描述了基于行为的机器人哲学。
  • The Simpsons(辛普森一家):这是一部著名的美国电视系列剧。“Stop, or My Dog Will Shoot!” 这一集讲述 Lisa Simpson 利用 Tremaux 的算法帮助全家逃出玉米地迷宫。
  • Building Robots with LEGO Mindstorms NXT(Dave Astolfo、Mario Ferrari 和 Giulio Ferrari,Syngress Publishing,2007 年):本书是 LEGO Mindstorms 系列中最好的两本之一,其中一章讨论了如何构建解迷宫机器人。
  • Geeks in Toyland”(Brendan I. Koerner,Wired,2006 年 2 月):这篇文章描述 LEGO Mindstorms NXT 的开发以及黑客如何帮助设计和开发。
  • Hacking Roomba(Tod E. Kurt,Wiley,2007 年):本书提供了许多有意思的 Roomba 技巧,比如让 Roomba 唱歌和画画儿,在其上安装 Linux®,把它当作鼠标使用(为什么不呢?)。
  • Turtle Geometry(Harold Abelson 和 Andrea diSessa,MIT Press,1986 年):本书包含关于迷宫的信息。
  • Fast, Cheap & Out of Control:Errol Morris 制作的这部记录片记录了 Rodney Brooks 的访谈(还有一位训兽师、园艺师和世界著名的无毛鼹鼠权威的访谈)。
  • Think Labyrinth: Maze Algorithms:迷宫专家 Walter D. Pullen 对迷宫和迷宫算法做了精彩的介绍。
  • Maze:Wikipedia 上有许多关于迷宫的文章。
  • 浏览 技术书店,查找关于这些主题和其他技术主题的图书。
  • developerWorks Java 技术专区:其中有数百篇关于 Java 编程各个方面的文章。

获得产品和技术

  • Algernon 是一个正在开发的开放源码项目。下载 本文使用的源代码。
  • Simbad:下载 Simbad 工具箱。

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Java technology
ArticleID=279602
ArticleTitle=机器人、迷宫和包容体系结构
publish-date=01112008