开源机器人技术工具包
nianqiu_liu2006/11/21软件综合 IP:四川

开源机器人技术工具包

使用虚拟舞台来测试机器人技术算法

级别: 中级

M. Tim Jones (mtj@XXXXXXXXXXX), 顾问工程师, Emulex

2006 年 10 月 08 日

构建机器人需要涉及很多学科的技能,包括嵌入式固件和硬件设计、传感器的选择、控制系统的设计,以及机械结构设计。而仿真环境可以为测试、评测和机器人技术算法的可视化提供一个虚拟的舞台,而不用花费高昂的开发成本(和时间)。本文将简要介绍一些 Linux 的开源机器人技术工具包及功能,并帮助我们判断哪些技术最适合我们使用。

传统机器人的类型有很多,形式也各式各样,但是随着软件代理(虚拟机器人)的出现,这些变种又大大扩充了。虚拟机器人借鉴了物理机器人的很多特性。例如,物理机器人的机动性意味着某种形式的移动,但是移动软件机器人(或 代理)也可以有机动性 —— 此处是指在网络上不同主机之间迁移的能力。图 1 给出了物理和虚拟领域中自治机器人的一个简单的分类。本文将着重使用 软件代理 作为在人工合成环境中仿真机器人的机制。

软件机器人?

华盛顿大学的研究人员创造了 Softbots 这个术语 —— 它是 软件(software机器人(robot 这两个术语混合产生的。术语 智能代理(intelligent agent 现在使用得更为广泛,尤其是在 Internet 实体内容中更是如此。在 1996 年,Franklin 和 Graesser 提出了第一种代理分类方法将病毒划分为自治代理。


1. 自治机器人的简单分类


<v:shapetype><v:stroke joinstyle="miter"></v:stroke><v:formulas><v:f eqn="if lineDrawn pixelLineWidth 0"></v:f><v:f eqn="sum @0 1 0"></v:f><v:f eqn="sum 0 0 @1"></v:f><v:f eqn="prod @2 1 2"></v:f><v:f eqn="prod @3 21600 pixelWidth"></v:f><v:f eqn="prod @3 21600 pixelHeight"></v:f><v:f eqn="sum @0 0 1"></v:f><v:f eqn="prod @6 1 2"></v:f><v:f eqn="prod @7 21600 pixelWidth"></v:f><v:f eqn="sum @8 21600 0"></v:f><v:f eqn="prod @7 21600 pixelHeight"></v:f><v:f eqn="sum @10 21600 0"></v:f></v:formulas><v:path connecttype="rect" gradientshapeok="t" extrusionok="f"></v:path><lock aspectratio="t" v:ext="edit"></lock></v:shapetype>

机器人的元素

不管我们是在讨论物理机器人还是虚拟(软件)机器人,有一些基本的概念都是相同的。机器人用一些传感器用来感知环境,用一些效应器来对环境进行操作,用一个控制系统让机器人按照我们期望的方式进行反应(参见图 2)。


2. 所有机器人系统的基本元素

在物理世界中,消防机器人可以使用温度传感器、红外(IR)传感器、全球定位系统(GPS)来感知环境,并使用发动机和灭火器作为效应器来对环境进行操作。而虚拟搜索代理则可以使用 Web 服务器和 HTTP 接口来感知环境(Internet)并对环境进行操作,并使用一个终端作为效应器来与用户进行通信。

图 3 所示的系统是一个封闭循环,传感器负责为控制系统提供数据,而控制系统则驱动着环境中因素的变化。考虑这个问题的另外一种方法是采用反馈这个术语。如果控制系统指定了一个改变环境的操作,那么传感器就可以验证发生了这种变化,并将环境的新状态反馈给控制系统。而开放循环系统只能假设操作成功改变了环境的状态,这永远都不是什么好事。


3. 对环境进行封闭循环操作

在构建机器人时,我们必须要从一个整体来考虑传感器、效应器和控制系统。对于本文来说,我们将着重介绍控制系统,以及在将其嵌入物理机器人之前可以对其进行仿真和验证的方法。

机器人技术和仿真

仿真在机器人技术领域扮演了一个关键的角色,因为它可以进行一些实验,如果没有仿真技术,这些实验可能会非常昂贵,而且需要花费大量的时间。仿真让我们可以在动态的合成环境中尝试一些做法,同时可以搜集这些响应数据,从而确定控制系统的品质。仿真还可以允许对机器人控制系统进行革新,这依赖于很多代控制系统的随机排列(遗传算法可以证实这一点)。

仿真的一大优点表现在多机器人的仿真中。为这些仿真提供的一个流行比赛是机器人足球赛,在这种比赛中,参赛选手可以使用仿真机器人,也可以使用物理机器人,一组机器人会与另外一组机器人展开世界上流行的英式足球较量(这使得这项比赛非常适合成为一项国际赛事)。机器人必须能够完全与整个团队中的其他机器人进行合作(可能相互之间需要进行通信),并与对手团队中的机器人展开竞赛,这对于机器人的行为是一项挑战性极强的测试。

但是对于仿真来说,也有些频感乏术的地方。真实的世界往往都非常凌乱,并且充满了各种噪音,因此这种合成环境基本上都很难进行建模。对机器人进行仿真通常都非常困难,因为真实世界中的传感器通常都可能呈现出不同的或非预期的特性。尽管有这些缺陷,我们依然可以通过在合成环境中对机器人进行仿真而学习到很多知识。

Linux 和机器人技术

Linux 是机器人技术采用的一个流行操作系统,因为从根源上来说,它与机器人技术的历史是类似的。机器人技术是一个实验领域。它是有关优化、尝试新事务和在将来不断革新的一种技术。Linux 从根本上来说,在这些方面也都具有相同的特性。早期的机器人都非常怪异,很少可以展示出实际应用价值。类似地,Linux 最开始也只是某些“瘾君子”的操作系统,但是它现在已经成长为一个功能强大而且十分稳定的操作系统了,我们在从嵌入式设备到超级计算机(包括很多机器人)上面都可以找到 Linux 的踪迹。

Linux 上的开源工具包

有几个开源工具包可以用来构建机器人控制系统。本文将介绍移动机器人仿真器和物理建模系统,最后再介绍一下一个可以支持在物理机器人中嵌入仿真控制系统的仿真器。这些工具包大部分都可以在 Linux 上运行,这主要是由于 Linux 的开源模型。开源软件是这样一个平台:我们在这种平台上只使用很少的努力就可以很快地开发软件,因此这种平台是非常理想的。Linux 也可以进行定制,这在其他操作系统中是不可能的(例如将内核做得最小并对内核进行扩充)。有关这些工具包的链接都在本文末尾 参考资料 一节中可以找到。

ODE

Rushide Smith 的 Open Dynamics Engine(ODE)是一个开源的物理引擎,使用它可以仿真铰接式固形体运动。这样不管我们使用哪种图形库(可能使用 OpenGL),都可以对真实对象的物理特性进行仿真。我们可以使用 ODE 来对合成环境中的各种对象进行建模,例如三维游戏环境中的人物或虚拟驾驶中的交通工具。除了速度快之外, ODE 还可以支持实时仿真的碰撞检测。

ODE 目前可以支持球窝、铰链、滑块、定轴、角电机和 hinge-2 (用于交通工具的连接)连接类型,以及其他一些连接类型。它还可以支持各种碰撞原语(例如球面碰撞和平面碰撞)和几个碰撞空间。

什么是铰接式固形体?

铰接式固形体(articulated rigid body 就是一个使用各种连接手段将各种图形连接在一起而构成的一个结构。例如,我们可以认为构成腿或交通工具底盘、悬吊系统、轮子等元素的装置都是连接。ODE 可以有效地对这些元素进行建模,包括摩擦力模型。

ODE 主要是使用 C++ 语言编写的,但是它也提供了 CC++ 的清晰接口来帮助我们与应用程序进行集成。有关 ODE 更好的一点是它发行所采用的许可证:LGPL(GNU Lesser General Public License)和 BSD License。在这两个许可证中,我们都可以在商业产品中使用 ODE 的源代码,而不需任何费用。其结果是我们在很多商业游戏、飞行模拟器和虚拟现实仿真中都可以找到 ODE。

清单 1 中的源代码给出了一个简单的例子,这个环境中中存在火星的地心引力,里面有一个球具有向上的速度。由于这个环境中具有地心引力,因此向上的速度不会坚持很久;最后这个球会上升到顶点并开始下降。在初始化完成之后(即在这个环境中创建对象并设置好属性之后),我们就可以通过 dWorldStep 对这个世界中的物理特性进行仿真了。要理解发生的现象,可以经常调用 dBodyGetPosition 并给它传入球的标识符来获得它的当前位置。


清单 1. 简单的 ODE 实验:地心引力环境中的球

<font><font>                <p></p></font></font>
<font><font>#include <iostream><p></p></iostream></font></font>
<font><font>#include <ode ode.h><p></p></ode></font></font>
<p><font> </font></p>
<font><font>#define time_step               (float)0.1<p></p></font></font>
<p><font> </font></p>
<font><font>int main()<p></p></font></font>
<font><font>{<p></p></font></font>
<font><font>  dWorldID myWorld_id;<p></p></font></font>
<font><font>  dBodyID mySphere_id;<p></p></font></font>
<font><font>  dMass sphereMass;<p></p></font></font>
<font><font>  const dReal *pos;<p></p></font></font>
<font><font>  float time = 0.0;<p></p></font></font>
<p><font> </font></p>
<font><font>  /* Create a new world */<p></p></font></font>
<font><font>  myWorld_id = </font></font><strong><font>dWorldCreate</font></strong><font><font>();<p></p></font></font>
<p><font> </font></p>
<font><font>  /* Create a sphere in the world */<p></p></font></font>
<font><font>  mySphere_id  = </font></font><strong><font>dBodyCreate</font></strong><font><font>( myWorld_id );<p></p></font></font>
<p><font> </font></p>
<font><font>  /* Set the world's global gravity vector (Mars) -- x,y,z */<p></p></font></font>
<font>  </font><strong><font>dWorldSetGravity</font></strong><font><font>( myWorld_id, 0, 0, -3.77 );<p></p></font></font>
<p><font> </font></p>
<font><font>  /* Set the Sphere's position in the world -- x,y,z */<p></p></font></font>
<font>  </font><strong><font>dBodySetPosition</font></strong><font><font>( mySphere_id, 0, 0, 100 );<p></p></font></font>
<p><font> </font></p>
<font><font>  /* Set the Sphere's mass (density, radius) */<p></p></font></font>
<font>  </font><strong><font>dMassSetSphere</font></strong><font><font>( &sphereMass, 1, 2 );<p></p></font></font>
<font>  </font><strong><font>dBodySetMass</font></strong><font><font>( mySphere_id, &sphereMass );<p></p></font></font>
<p><font> </font></p>
<font><font>  /* Give the sphere a small amount of upward (z) velocity */<p></p></font></font>
<font>  </font><strong><font>dBodySetLinearVel</font></strong><font><font>( mySphere_id, 0.0, 0.0, 5.0 );<p></p></font></font>
<p><font> </font></p>
<font><font>  /* Run the simulation */<p></p></font></font>
<font><font>  while (time < 5.0) {<p></p></font></font>
<p><font> </font></p>
<font><font>    /* Simulate the world for the defined time-step */<p></p></font></font>
<font>    </font><strong><font>dWorldStep</font></strong><font><font>( myWorld_id, time_step );<p></p></font></font>
<p><font> </font></p>
<font><font>    /* Get the current position of the sphere */<p></p></font></font>
<font><font>    pos = </font></font><strong><font>dBodyGetPosition</font></strong><font><font>( mySphere_id );<p></p></font></font>
<p><font> </font></p>
<font><font>    std::cout << "position (" << pos[0] << ", "<p></p></font></font>
<font><font>             << pos[1] << ", " << pos[2] << ")\n";<p></p></font></font>
<p><font> </font></p>
<font><font>    /* Next time step */<p></p></font></font>
<font><font>    time += time_step;<p></p></font></font>
<p><font> </font></p>
<font><font>  }<p></p></font></font>
<p><font> </font></p>
<font><font>  /* Destroy the objects */<p></p></font></font>
<font>  </font><strong><font>dBodyDestroy</font></strong><font><font>( mySphere_id );<p></p></font></font>
<font>  </font><strong><font>dWorldDestroy</font></strong><font><font>( myWorld_id );<p></p></font></font>
<p><font> </font></p>
<font><font>  return 0;<p></p></font></font>
<font><font>}<p></p></font></font>

因此,如果我们需要一个工业级质量的物理引擎(它可以在 Linux 上运行,也可以在其他平台上运行)来仿真移动机器人或现实环境中无人驾驶交通工具,那么 ODE 就是一个极好的选择。与 OpenGL 应用程序编程接口(API)一起使用,ODE 就可以生成由真实相片构成的图形,并且具有真实的物理特性。

Simbad 机器人仿真器

Simbad 是一个使用 Java® 编程语言编写的三维机器人仿真器(因此它可以在 Linux 或其他支持 Java 虚拟机(或 JVM)的平台上运行);不过这个仿真器还包括了对 Python 脚本语言(通过 Jython)的支持。Simbad 设计用来研究自治机器人环境中的人工智能(AI)算法,它包括了一个 rich GUI(图形用户界面)进行可视化操作,它不但是对机器人的动作进行可视化,而且还是从机器人的角度来进行可视化的。

使得 Simbad 如此有趣的原因是它的使用非常简单,可以让我们快速创建新机器人的行为。不过尽管为 Simbad 的开发都非常简单,但是它实际上是机器人仿真的一个扩展框架。

使用这个仿真器,我们可以创建环境或对环境进行裁减,然后使用各种传感器来开发自己的机器人控制器。可用的传感器包括视觉传感器(彩色照相机)、范围传感器(声波和红外探测器)以及碰撞检测的缓冲。

这些传感器的 API 都非常清晰直观,易于使用。清单 2 中的例子展示了仿真声波用法,以及如何检测命中(对象检测到了)。


清单 2. 展示仿真声波用法的代码片段

<font><font>int sonar_id, total_sonars;<p></p></font></font>
<p><font> </font></p>
<font><font>// If at least one sensor has a hit<p></p></font></font>
<font>if (</font><strong><font>sonars.oneHasHit</font></strong><font><font>()) {<p></p></font></font>
<p><font> </font></p>
<font><font>  // Find out how many sonars are on the robot<p></p></font></font>
<font><font>  total_sonars = </font></font><strong><font>sonars.getNumSensors</font></strong><font><font>();<p></p></font></font>
<p><font> </font></p>
<font><font>  // Iterate through each sonar<p></p></font></font>
<font><font>  for ( sonar_id = 0 ; sonar_id < total_sonars ; sonar_id++ ) {<p></p></font></font>
<p><font> </font></p>
<font><font>    // Does this one have a hit?<p></p></font></font>
<font><font>    if (</font></font><strong><font>sonars.hasHit</font></strong><font><font>(sonar_id)) {<p></p></font></font>
<p><font> </font></p>
<font><font>      // Emit the details (angle, range)<p></p></font></font>
<font><font>      System.out.println( "Sonar hit at angle " +<p></p></font></font>
<font>                           </font><strong><font>sonars.getAngle</font></strong><font><font>(i) +<p></p></font></font>
<font><font>                           " at range " +<p></p></font></font>
<font>                           </font><strong><font>sonars.getMeasurement</font></strong><font><font>(i) );<p></p></font></font>
<p><font> </font></p>
<font><font>    }<p></p></font></font>
<font><font>  }<p></p></font></font>
<font><font>}<p></p></font></font>

Simbad 中的其他传感器也遵循类似的模式,它们也创建了一组直观的 API。

让 Simbad 变得如此有用的是它对机器人仿真和可视化而使用的终端。正如图 4 所显示的一样,Simbad 的终端给我们提供了世界的一个实时视图,一个提供了机器人详细信息(包括照相机)的监视器面板,以及一个用来对仿真进行管理的控制面板。


4. Simbad 机器人仿真器和可视化终端

Simbad 还提供了很好的文档和教程来帮助我们迅速入门,并使用 Java 和 Python 语言快速运行它。除了单一机器人仿真之外,我们还可以同时对多个机器人进行仿真。总体上来说,Simbad 仿真器是一个非常适合测试智能机器人算法的环境。Simbad 可以按照 GPL 开源许可证的方式使用。

TeamBots

TeamBots 是一个可移植的多代理机器人仿真器,它可以支持动态环境中多代理控制系统的仿真,并可以提供可视化功能。与 Simbad 之类的其他仿真器相比,TeamBots 有一点非常独特:它的控制系统具有很好的可移植性。我们可以开发自己的控制系统,并将其在仿真器上进行验证,然后再在真正的移动机器人上对控制系统进行测试(使用 Nomadic Technologies Nomad 150 机器人)。

TeamBots API 为控制系统提供了一个抽象层(参见图 5)。结果是,控制系统无法知道到底是运行在合成环境(TBSim)的仿真器内还是运行在真实环境(TBHard)的移动机器人平台内。


5. 控制系统的 TeamBots API 抽象层

TeamBots 仿真环境非常灵活,也很容易用来使用对象和其他机器人构建合成环境。我们很容易就可以添加墙、任意对象、路以及其他运行相同或不同控制系统的机器人。通过这种方式,我们可以构建捕食/被捕食仿真(这是是一个例子)。另外,对象不需要是静态的。我们可以添加在环境中四处移动的对象,或者机器人推动的对象(例如球)。

使用 TeamBots,我们可以对不同类型的机器人仿真进行建模。例如,在 1997 年,Georgia Tech 使用 TeamBots 赢得了美国人工智能协会(AAAI)的移动机器人竞赛,他在一个动态环境中使用了两个仿真 Nomad 150 机器人进行填料。这两个机器人的目标是对环境进行搜索,然后将蓝色对象放到蓝色柜子中,将橙色对象放到橙色柜子中(参见图 6)。为了增加竞赛的复杂性,橙色球是动态的,并且在整个环境中是不断移动的。


6. TeamBots 的填料仿真

在图 6 中,移动机器人 1 拿到一个蓝色对象,正在向一个蓝色柜子移动,以便将这个蓝色球放到蓝色柜子中。机器人 0 正在进行搜索。

我们在机器人英式足球选手的开发中也可以使用 TeamBots。英式足球是一项国际流行的运动,它是国际上很多大学和组织进行竞争的一个很好的平台。机器人英式足球比赛的规则可能会稍有不同(尤其是在考虑到移动平台、双足平台或 Sony Aibo 之间的差异时),但是它们都使用了这项赛事的相同的基本模型。

在图 7 中,机器人 1(黄/白)正在冲向球,试图射门。机器人 0(蓝/红)正在守门,它处于一个阻断的位置。机器人英式足球赛看起来实际上非常有趣,TeamBots 发行版提供了几个球队,我们可以使用它们来体验一下新策略。


7. TeamBots SoccerBots 中的展示

TeamBots 为英式足球塞提供了一个 Java API,它主要关注的是球员的 “大脑”。效应器 API 允许对机器人进行调优,以特定的速度移动、踢球或简单地带球。传感器是在高层构建的,它们提供了 API 来确定球的方向、其他球员(队友和对手)的方向、目前冲锋在前面的人、守门员的位置等等。

为了让您对 TeamBots Soccer API 有个大概的了解,参见清单 3,它给出了一个非常简单的策略。这个策略(源自于 Tucker Balch 编写的 SoccerBots 源代码)简单地寻找球、冲向这个球,然后踢球(不管是否踢向球门)。这是一个随机策略,但是它展示了这个 API 的简单性。


清单 3. 使用 TeamBots SoccerBots API 的简单英式足球球员的代码片段

<font><font>public int TakeStep()<p></p></font></font>
<font><font>{<p></p></font></font>
<font><font>  Vec2 ball;<p></p></font></font>
<font><font>  long T;<p></p></font></font>
<p><font> </font></p>
<font><font>  T = </font></font><strong><font>abstract_robot.getTime</font></strong><font><font>();<p></p></font></font>
<p><font> </font></p>
<font><font>  // Get the vector to the ball<p></p></font></font>
<font><font>  ball = </font></font><strong><font>abstract_robot.getBall</font></strong><font><font>(T);<p></p></font></font>
<p><font> </font></p>
<font><font>  // Point ourselves to it<p></p></font></font>
<font>  </font><strong><font>abstract_robot.setSteerHeading</font></strong><font><font>(T, ball.t);<p></p></font></font>
<p><font> </font></p>
<font><font>  // Go to it (maximum speed)<p></p></font></font>
<font>  </font><strong><font>abstract_robot.setSpeed</font></strong><font><font>(T, 1.0);<p></p></font></font>
<p><font> </font></p>
<font><font>  // If we can kick it, do so!<p></p></font></font>
<font><font>  if (</font></font><strong><font>abstract_robot.canKick<p></p></font></strong>
<font><font>(T)) <p></p></font></font>
<strong><font>abstract_robot.kick</font></strong><font><font>(T);<p></p></font></font>
<p><font> </font></p>
<font><font>  return(CSSTAT_OK);<p></p></font></font>

}

TeamBots 发行版对于移动机器人的原型和仿真来说都是一个很好的环境,也非常适合在使用 TBHard 环境的真实机器人中使用它们。TeamBots 是开源的(由 Georgia Tech 和 <st1:place w:st="on"><st1[s:7]laceName w:st="on">Carnegie</st1[s:7]laceName> <st1[s:7]laceName w:st="on">Mellon</st1[s:7]laceName> <st1[s:7]laceType w:st="on">University</st1[s:7]laceType></st1:place> 的 Tucker Balch 开发),可以自由用于教育和研究目的。这个仿真器是使用 Java 语言开发的,发行版本中提供了完整的源代码和几个例子,可以帮助我们快速入门。

其他工具包

有很多仿真器都为之编写的最知名的移动机器人平台之一是 Khepera。不幸的是,Khepera 已经变成了一个商业软件,而不再是开源的了。所幸的是,诸如 KControl 之类的工具包依然可以用来在 Linux 系统上为 Khepera 开发控制系统。

在 Gazebo 中有一个有趣的可以运动的三维机器人仿真器。Gazebo 不但可以对标准的机器人传感器(例如惯性度量单元、GPS 接收器和单筒相机)进行建模,而且还可以对机器人环境中使用的真实固形体物理部件进行建模。Gazebo 可以支持一个插件模型,在这个模型中我们可以将新的机器人传感器模型动态加载到环境中。

最后,一个非常有用的机器人导航工具包是 Carmen —— Carnegie Mellon Robot Navigation Toolkit。Carmen 实现了一个模块化的架构,它提供了基本的导航原语,例如故障排除、路径规划和绘图。除了提供一个两维仿真器之外,Carmen 还可以支持几个在 Linux 上运行的物理机器人平台。

构建 Linux 机器人

开始构建基于 Linux 的机器人并不像我们想像的那样困难。实际上,有些高中的科学课程正在使用 Linux 和现有的可用硬件作为基于 Linux 的机器人的核心。例如,可以使用老式的 PC 主板作为系统核心(或者稍微好些的,使用老式的笔记本),并从 USB 磁盘上来引导 Linux(这比 CD-ROM 或硬盘/软盘引导消耗的电量会少很多)。板载的并口可以很容易转换成很多设备,例如将输入和输出分离开,或者驱动一组步进马达。串口可以用来接收 GPS 坐标,或者连接外部设备,例如 A/D (模拟信号到数字信号)或 D/A(数字信号到模拟信号)转换器。最后,可以购买一些便宜的 USB Web 相机来为机器人添加视觉功能。

但是真正使得 Linux 在这个环境中大放异彩的是它对环境的简化能力,从而使得所有人都可以通过 Python 之类的高级语言进行机器人控制系统的设计。位于 <st1:place w:st="on"><st1:State w:st="on">Maine</st1:State></st1:place> 的 Greater Houlton Christian Academy 的 Michael Surran 最近(已经是第二年了)教授了一门高中的机器人课程,它充分利用了 Linux 和目前可用的硬件。这个课程的核心就是使用 Python。由于 Python 是一种解释语言,因此很容易来试验算法,而不需要经过长时间的编译过程(这点使得解释脚本语言显得如此有用)。

如果我们正在寻找最新的 Linux 解决方案,Carnegie Mellon University 最近在自己的 Mobile Robot Programming Lab(MRPL)中推出了 “Qwerkbot” 平台,它可以在 2.6 版本的 Linux 内核上运行。“Qwerkbot” 是一个基于 ARM9 的主板,具有 8MB 的闪存和 32MB 的SDRAM;它包括了 4 个板载的马达控制器、16 个伺服控制器、16 个数字 I/O、8 个 12 位的模拟输入,以及其他组件。

结束语

机器人仿真器可以极大地简化构建物理机器人的工作。使用这些仿真器,我们可以在将自己的想法和策略放入硬件之前就对它们进行测试。幸运的是,Linux 和开源社区有很多选择,它们不但非常容易使用,而且还可以支持与硬件平台的直接链接。







2517_17469_1569.gif

2517_17469_1570.gif

2517_17469_1571.gif

2517_17469_1572.jpg

2517_17469_1573.gif

2517_17469_1574.jpg

2517_17469_1575.jpg

来自:计算机科学 / 软件综合
0
已屏蔽 原因:{{ notice.reason }}已屏蔽
{{notice.noticeContent}}
~~空空如也

想参与大家的讨论?现在就 登录 或者 注册

所属专业
所属分类
上级专业
同级专业
文件下载
加载中...
{{errorInfo}}
{{downloadWarning}}
你在 {{downloadTime}} 下载过当前文件。
文件名称:{{resource.defaultFile.name}}
下载次数:{{resource.hits}}
上传用户:{{uploader.username}}
所需积分:{{costScores}},{{holdScores}}下载当前附件免费{{description}}
积分不足,去充值
文件已丢失

当前账号的附件下载数量限制如下:
时段 个数
{{f.startingTime}}点 - {{f.endTime}}点 {{f.fileCount}}
视频暂不能访问,请登录试试
仅供内部学术交流或培训使用,请先保存到本地。本内容不代表科创观点,未经原作者同意,请勿转载。
音频暂不能访问,请登录试试
支持的图片格式:jpg, jpeg, png
插入公式
评论控制
加载中...
文号:{{pid}}
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

加载中...
详情
详情
推送到专栏从专栏移除
设为匿名取消匿名
查看作者
回复
只看作者
加入收藏取消收藏
收藏
取消收藏
折叠回复
置顶取消置顶
评学术分
鼓励
设为精选取消精选
管理提醒
编辑
通过审核
评论控制
退修或删除
历史版本
违规记录
投诉或举报
加入黑名单移除黑名单
查看IP
{{format('YYYY/MM/DD HH:mm:ss', toc)}}