roby 0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +29 -0
- data/History.txt +4 -0
- data/License-fr.txt +519 -0
- data/License.txt +515 -0
- data/Manifest.txt +245 -0
- data/NOTES +4 -0
- data/README.txt +163 -0
- data/Rakefile +161 -0
- data/TODO.txt +146 -0
- data/app/README.txt +24 -0
- data/app/Rakefile +8 -0
- data/app/config/ROBOT.rb +5 -0
- data/app/config/app.yml +91 -0
- data/app/config/init.rb +7 -0
- data/app/config/roby.yml +3 -0
- data/app/controllers/.gitattributes +0 -0
- data/app/controllers/ROBOT.rb +2 -0
- data/app/data/.gitattributes +0 -0
- data/app/planners/ROBOT/main.rb +6 -0
- data/app/planners/main.rb +5 -0
- data/app/scripts/distributed +3 -0
- data/app/scripts/generate/bookmarks +3 -0
- data/app/scripts/replay +3 -0
- data/app/scripts/results +3 -0
- data/app/scripts/run +3 -0
- data/app/scripts/server +3 -0
- data/app/scripts/shell +3 -0
- data/app/scripts/test +3 -0
- data/app/tasks/.gitattributes +0 -0
- data/app/tasks/ROBOT/.gitattributes +0 -0
- data/bin/roby +210 -0
- data/bin/roby-log +168 -0
- data/bin/roby-shell +25 -0
- data/doc/images/event_generalization.png +0 -0
- data/doc/images/exception_propagation_1.png +0 -0
- data/doc/images/exception_propagation_2.png +0 -0
- data/doc/images/exception_propagation_3.png +0 -0
- data/doc/images/exception_propagation_4.png +0 -0
- data/doc/images/exception_propagation_5.png +0 -0
- data/doc/images/replay_handler_error.png +0 -0
- data/doc/images/replay_handler_error_0.png +0 -0
- data/doc/images/replay_handler_error_1.png +0 -0
- data/doc/images/roby_cycle_overview.png +0 -0
- data/doc/images/roby_replay_02.png +0 -0
- data/doc/images/roby_replay_03.png +0 -0
- data/doc/images/roby_replay_04.png +0 -0
- data/doc/images/roby_replay_event_representation.png +0 -0
- data/doc/images/roby_replay_first_state.png +0 -0
- data/doc/images/roby_replay_relations.png +0 -0
- data/doc/images/roby_replay_startup.png +0 -0
- data/doc/images/task_event_generalization.png +0 -0
- data/doc/papers.rdoc +11 -0
- data/doc/styles/allison.css +314 -0
- data/doc/styles/allison.js +316 -0
- data/doc/styles/allison.rb +276 -0
- data/doc/styles/jamis.rb +593 -0
- data/doc/tutorials/01-GettingStarted.rdoc +86 -0
- data/doc/tutorials/02-GoForward.rdoc +220 -0
- data/doc/tutorials/03-PlannedPath.rdoc +268 -0
- data/doc/tutorials/04-EventPropagation.rdoc +236 -0
- data/doc/tutorials/05-ErrorHandling.rdoc +319 -0
- data/doc/tutorials/06-Overview.rdoc +40 -0
- data/doc/videos.rdoc +69 -0
- data/ext/droby/dump.cc +175 -0
- data/ext/droby/extconf.rb +3 -0
- data/ext/graph/algorithm.cc +746 -0
- data/ext/graph/extconf.rb +7 -0
- data/ext/graph/graph.cc +529 -0
- data/ext/graph/graph.hh +183 -0
- data/ext/graph/iterator_sequence.hh +102 -0
- data/ext/graph/undirected_dfs.hh +226 -0
- data/ext/graph/undirected_graph.hh +421 -0
- data/lib/roby.rb +41 -0
- data/lib/roby/app.rb +870 -0
- data/lib/roby/app/rake.rb +56 -0
- data/lib/roby/app/run.rb +14 -0
- data/lib/roby/app/scripts/distributed.rb +13 -0
- data/lib/roby/app/scripts/generate/bookmarks.rb +162 -0
- data/lib/roby/app/scripts/replay.rb +31 -0
- data/lib/roby/app/scripts/results.rb +15 -0
- data/lib/roby/app/scripts/run.rb +26 -0
- data/lib/roby/app/scripts/server.rb +18 -0
- data/lib/roby/app/scripts/shell.rb +88 -0
- data/lib/roby/app/scripts/test.rb +40 -0
- data/lib/roby/basic_object.rb +151 -0
- data/lib/roby/config.rb +5 -0
- data/lib/roby/control.rb +747 -0
- data/lib/roby/decision_control.rb +17 -0
- data/lib/roby/distributed.rb +32 -0
- data/lib/roby/distributed/base.rb +440 -0
- data/lib/roby/distributed/communication.rb +871 -0
- data/lib/roby/distributed/connection_space.rb +592 -0
- data/lib/roby/distributed/distributed_object.rb +206 -0
- data/lib/roby/distributed/drb.rb +62 -0
- data/lib/roby/distributed/notifications.rb +539 -0
- data/lib/roby/distributed/peer.rb +550 -0
- data/lib/roby/distributed/protocol.rb +529 -0
- data/lib/roby/distributed/proxy.rb +343 -0
- data/lib/roby/distributed/subscription.rb +311 -0
- data/lib/roby/distributed/transaction.rb +498 -0
- data/lib/roby/event.rb +897 -0
- data/lib/roby/exceptions.rb +234 -0
- data/lib/roby/executives/simple.rb +30 -0
- data/lib/roby/graph.rb +166 -0
- data/lib/roby/interface.rb +390 -0
- data/lib/roby/log.rb +3 -0
- data/lib/roby/log/chronicle.rb +303 -0
- data/lib/roby/log/console.rb +72 -0
- data/lib/roby/log/data_stream.rb +197 -0
- data/lib/roby/log/dot.rb +279 -0
- data/lib/roby/log/event_stream.rb +151 -0
- data/lib/roby/log/file.rb +340 -0
- data/lib/roby/log/gui/basic_display.ui +83 -0
- data/lib/roby/log/gui/chronicle.rb +26 -0
- data/lib/roby/log/gui/chronicle_view.rb +40 -0
- data/lib/roby/log/gui/chronicle_view.ui +70 -0
- data/lib/roby/log/gui/data_displays.rb +172 -0
- data/lib/roby/log/gui/data_displays.ui +155 -0
- data/lib/roby/log/gui/notifications.rb +26 -0
- data/lib/roby/log/gui/relations.rb +248 -0
- data/lib/roby/log/gui/relations.ui +123 -0
- data/lib/roby/log/gui/relations_view.rb +185 -0
- data/lib/roby/log/gui/relations_view.ui +149 -0
- data/lib/roby/log/gui/replay.rb +327 -0
- data/lib/roby/log/gui/replay_controls.rb +200 -0
- data/lib/roby/log/gui/replay_controls.ui +259 -0
- data/lib/roby/log/gui/runtime.rb +130 -0
- data/lib/roby/log/hooks.rb +185 -0
- data/lib/roby/log/logger.rb +202 -0
- data/lib/roby/log/notifications.rb +244 -0
- data/lib/roby/log/plan_rebuilder.rb +470 -0
- data/lib/roby/log/relations.rb +1056 -0
- data/lib/roby/log/server.rb +550 -0
- data/lib/roby/log/sqlite.rb +47 -0
- data/lib/roby/log/timings.rb +164 -0
- data/lib/roby/plan-object.rb +247 -0
- data/lib/roby/plan.rb +762 -0
- data/lib/roby/planning.rb +13 -0
- data/lib/roby/planning/loops.rb +302 -0
- data/lib/roby/planning/model.rb +906 -0
- data/lib/roby/planning/task.rb +151 -0
- data/lib/roby/propagation.rb +562 -0
- data/lib/roby/query.rb +619 -0
- data/lib/roby/relations.rb +583 -0
- data/lib/roby/relations/conflicts.rb +70 -0
- data/lib/roby/relations/ensured.rb +20 -0
- data/lib/roby/relations/error_handling.rb +23 -0
- data/lib/roby/relations/events.rb +9 -0
- data/lib/roby/relations/executed_by.rb +193 -0
- data/lib/roby/relations/hierarchy.rb +239 -0
- data/lib/roby/relations/influence.rb +10 -0
- data/lib/roby/relations/planned_by.rb +63 -0
- data/lib/roby/robot.rb +7 -0
- data/lib/roby/standard_errors.rb +218 -0
- data/lib/roby/state.rb +5 -0
- data/lib/roby/state/events.rb +221 -0
- data/lib/roby/state/information.rb +55 -0
- data/lib/roby/state/pos.rb +110 -0
- data/lib/roby/state/shapes.rb +32 -0
- data/lib/roby/state/state.rb +353 -0
- data/lib/roby/support.rb +92 -0
- data/lib/roby/task-operations.rb +182 -0
- data/lib/roby/task.rb +1618 -0
- data/lib/roby/test/common.rb +399 -0
- data/lib/roby/test/distributed.rb +214 -0
- data/lib/roby/test/tasks/empty_task.rb +9 -0
- data/lib/roby/test/tasks/goto.rb +36 -0
- data/lib/roby/test/tasks/simple_task.rb +23 -0
- data/lib/roby/test/testcase.rb +519 -0
- data/lib/roby/test/tools.rb +160 -0
- data/lib/roby/thread_task.rb +87 -0
- data/lib/roby/transactions.rb +462 -0
- data/lib/roby/transactions/proxy.rb +292 -0
- data/lib/roby/transactions/updates.rb +139 -0
- data/plugins/fault_injection/History.txt +4 -0
- data/plugins/fault_injection/README.txt +37 -0
- data/plugins/fault_injection/Rakefile +18 -0
- data/plugins/fault_injection/TODO.txt +0 -0
- data/plugins/fault_injection/app.rb +52 -0
- data/plugins/fault_injection/fault_injection.rb +89 -0
- data/plugins/fault_injection/test/test_fault_injection.rb +84 -0
- data/plugins/subsystems/README.txt +40 -0
- data/plugins/subsystems/Rakefile +18 -0
- data/plugins/subsystems/app.rb +171 -0
- data/plugins/subsystems/test/app/README +24 -0
- data/plugins/subsystems/test/app/Rakefile +8 -0
- data/plugins/subsystems/test/app/config/app.yml +71 -0
- data/plugins/subsystems/test/app/config/init.rb +9 -0
- data/plugins/subsystems/test/app/config/roby.yml +3 -0
- data/plugins/subsystems/test/app/planners/main.rb +20 -0
- data/plugins/subsystems/test/app/scripts/distributed +3 -0
- data/plugins/subsystems/test/app/scripts/replay +3 -0
- data/plugins/subsystems/test/app/scripts/results +3 -0
- data/plugins/subsystems/test/app/scripts/run +3 -0
- data/plugins/subsystems/test/app/scripts/server +3 -0
- data/plugins/subsystems/test/app/scripts/shell +3 -0
- data/plugins/subsystems/test/app/scripts/test +3 -0
- data/plugins/subsystems/test/app/tasks/services.rb +15 -0
- data/plugins/subsystems/test/test_subsystems.rb +71 -0
- data/test/distributed/test_communication.rb +178 -0
- data/test/distributed/test_connection.rb +282 -0
- data/test/distributed/test_execution.rb +373 -0
- data/test/distributed/test_mixed_plan.rb +341 -0
- data/test/distributed/test_plan_notifications.rb +238 -0
- data/test/distributed/test_protocol.rb +516 -0
- data/test/distributed/test_query.rb +102 -0
- data/test/distributed/test_remote_plan.rb +491 -0
- data/test/distributed/test_transaction.rb +463 -0
- data/test/mockups/tasks.rb +27 -0
- data/test/planning/test_loops.rb +380 -0
- data/test/planning/test_model.rb +427 -0
- data/test/planning/test_task.rb +106 -0
- data/test/relations/test_conflicts.rb +42 -0
- data/test/relations/test_ensured.rb +38 -0
- data/test/relations/test_executed_by.rb +149 -0
- data/test/relations/test_hierarchy.rb +158 -0
- data/test/relations/test_planned_by.rb +54 -0
- data/test/suite_core.rb +24 -0
- data/test/suite_distributed.rb +9 -0
- data/test/suite_planning.rb +3 -0
- data/test/suite_relations.rb +8 -0
- data/test/test_bgl.rb +508 -0
- data/test/test_control.rb +399 -0
- data/test/test_event.rb +894 -0
- data/test/test_exceptions.rb +592 -0
- data/test/test_interface.rb +37 -0
- data/test/test_log.rb +114 -0
- data/test/test_log_server.rb +132 -0
- data/test/test_plan.rb +584 -0
- data/test/test_propagation.rb +210 -0
- data/test/test_query.rb +266 -0
- data/test/test_relations.rb +180 -0
- data/test/test_state.rb +414 -0
- data/test/test_support.rb +16 -0
- data/test/test_task.rb +938 -0
- data/test/test_testcase.rb +122 -0
- data/test/test_thread_task.rb +73 -0
- data/test/test_transactions.rb +569 -0
- data/test/test_transactions_proxy.rb +198 -0
- metadata +570 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
{Next tutorial}[link:files/doc/tutorials/02-GoForward_rdoc.html]
|
|
2
|
+
= Getting started
|
|
3
|
+
|
|
4
|
+
== Initializing an empty Roby application
|
|
5
|
+
Go into a shell into the directory you want your application in and run
|
|
6
|
+
$ roby init
|
|
7
|
+
creating tasks/
|
|
8
|
+
creating tasks/.gitattributes
|
|
9
|
+
creating scripts/
|
|
10
|
+
creating scripts/test
|
|
11
|
+
creating scripts/shell
|
|
12
|
+
creating scripts/server
|
|
13
|
+
creating scripts/run
|
|
14
|
+
creating scripts/results
|
|
15
|
+
creating scripts/replay
|
|
16
|
+
creating scripts/generate/
|
|
17
|
+
creating scripts/generate/bookmarks
|
|
18
|
+
creating scripts/distributed
|
|
19
|
+
creating planners/
|
|
20
|
+
creating planners/main.rb
|
|
21
|
+
creating data/
|
|
22
|
+
creating data/.gitattributes
|
|
23
|
+
creating controllers/
|
|
24
|
+
creating controllers/.gitattributes
|
|
25
|
+
creating config/
|
|
26
|
+
creating config/roby.yml
|
|
27
|
+
creating config/init.rb
|
|
28
|
+
creating config/app.yml
|
|
29
|
+
creating Rakefile
|
|
30
|
+
creating README.txt
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
You can see that the following directories are created:
|
|
34
|
+
tasks:: definition of task models
|
|
35
|
+
planners:: definition of planner models
|
|
36
|
+
controllers:: definition of the robot controllers
|
|
37
|
+
data:: data files
|
|
38
|
+
config:: robots configurations
|
|
39
|
+
test:: the test suites
|
|
40
|
+
log:: the log files (output of the last run)
|
|
41
|
+
results:: sets of logs that have been saved by scripts/results
|
|
42
|
+
scripts:: the standard Roby tools. Call them with --help to know what they are doing
|
|
43
|
+
|
|
44
|
+
The .gitattributes in empty directories is a trick allowing to commit an
|
|
45
|
+
empty Roby application with +git+ using
|
|
46
|
+
git init
|
|
47
|
+
git add .
|
|
48
|
+
git commit
|
|
49
|
+
|
|
50
|
+
Without it, +git+ would ignore those directories.
|
|
51
|
+
|
|
52
|
+
== Overview of Roby applications structure
|
|
53
|
+
|
|
54
|
+
In a single Roby application, someone can define multiple specific
|
|
55
|
+
_controllers_, tailored for specific robots. A specific Roby controller is
|
|
56
|
+
defined by a /robot name/ and a /robot type/. These two parameters define what
|
|
57
|
+
models and what configuration files the system will load on startup. Both
|
|
58
|
+
models and configuration files can be sorted into:
|
|
59
|
+
* a set common to all robots and robot types
|
|
60
|
+
* a set specific to all robots of the same type
|
|
61
|
+
* a set specific to a single robot
|
|
62
|
+
|
|
63
|
+
See Roby::Application for more details on the configuration/models loading logic.
|
|
64
|
+
|
|
65
|
+
== Creating a simple robot
|
|
66
|
+
|
|
67
|
+
During the tutorials, we will be creating different robots which is done by the
|
|
68
|
+
<tt>roby robot</tt> command. For instance, run
|
|
69
|
+
$ roby robot EmptyRobot
|
|
70
|
+
creating planners/EmptyRobot/
|
|
71
|
+
creating planners/EmptyRobot/main.rb
|
|
72
|
+
creating tasks/EmptyRobot/
|
|
73
|
+
creating tasks/EmptyRobot/.gitattributes
|
|
74
|
+
creating controllers/EmptyRobot.rb
|
|
75
|
+
creating config/EmptyRobot.rb
|
|
76
|
+
|
|
77
|
+
This creates the basic templates for the robot named EmptyRobot. The following
|
|
78
|
+
tutorials will explain their role to you.
|
|
79
|
+
|
|
80
|
+
= Next tutorial
|
|
81
|
+
|
|
82
|
+
{The next tutorial}[link:files/doc/tutorials/02-GoForward_rdoc.html] will show you
|
|
83
|
+
the basic plan model used by Roby and some central tool, which allow to execute the
|
|
84
|
+
Roby applications and to interact/control them remotely.
|
|
85
|
+
---
|
|
86
|
+
vim: tw=80
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
{Previous tutorial}[link:files/doc/tutorials/01-GettingStarted_rdoc.html]
|
|
2
|
+
{Next tutorial}[link:files/doc/tutorials/03-PlannedPath_rdoc.html]
|
|
3
|
+
= The GoForward tutorial: making a simple robot move
|
|
4
|
+
This tutorial will make you create a simulated robot controller which makes the
|
|
5
|
+
robot go forward at constant speed. It will show you what a task is, how to
|
|
6
|
+
start a Roby controller and how to interact with it using the Roby shell.
|
|
7
|
+
|
|
8
|
+
First, we will define a GoForward task model, represented by a subclass
|
|
9
|
+
of Roby::Task. Tasks, which are instances of this model, have two roles:
|
|
10
|
+
* they _represent_ the 'go forward' activity in the plan. i.e. it represents
|
|
11
|
+
its properties, allowing to assess that its execution is going well
|
|
12
|
+
* they actually _make_ the robot 'go forward': they execute the code necessary
|
|
13
|
+
to do it, or activate an external process which will do that.
|
|
14
|
+
|
|
15
|
+
What we will see here is the second point. The first point will be discussed in
|
|
16
|
+
more details in the fourth tutorial: {error handling}[link:files/doc/tutorials/04-ErrorHandling_rdoc.html]
|
|
17
|
+
|
|
18
|
+
= A first attempt
|
|
19
|
+
== Writing the robot task model, and writing the robot controller
|
|
20
|
+
Edit <tt>tasks/go_forward.rb</tt> and add
|
|
21
|
+
class GoForward < Roby::Task
|
|
22
|
+
# The GoForward task needs the robot speed to be specified
|
|
23
|
+
arguments :speed
|
|
24
|
+
|
|
25
|
+
# Block called at every execution loop. It simulates the robot moving at
|
|
26
|
+
# the specified speed.
|
|
27
|
+
poll do
|
|
28
|
+
State.pos.x += speed
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# No specific action should be taken to make the task stop
|
|
32
|
+
terminates
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
Now, create the robot we will be working on. In the roby application we
|
|
36
|
+
have created in {the first tutorial}[link:files/doc/tutorials/01-GettingStarted_rdoc.html],
|
|
37
|
+
run
|
|
38
|
+
roby robot goForward
|
|
39
|
+
|
|
40
|
+
And in the controller file, <tt>controllers/goForward.rb</tt> do
|
|
41
|
+
# Define the original value of x
|
|
42
|
+
State.pos.x = 0
|
|
43
|
+
|
|
44
|
+
# Will display the value of x every 1 second
|
|
45
|
+
Roby.every(1) do
|
|
46
|
+
puts State.pos.x
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Create the task and start moving !
|
|
50
|
+
Roby.plan.insert(go = GoForward.new(:speed => 0.1))
|
|
51
|
+
puts "Going forward at speed #{go.speed}"
|
|
52
|
+
go.start!
|
|
53
|
+
|
|
54
|
+
You can then start the robot controller with <tt>scripts/run</tt> and stop it with CTRL+C.
|
|
55
|
+
|
|
56
|
+
$ scripts/run goForward
|
|
57
|
+
335705:25:08.324 (goForward) loading controller file /home/doudou/dev/roby-tutorials/controllers/goForward.rb
|
|
58
|
+
Going forward at speed 0.1
|
|
59
|
+
335705:25:08.356 (goForward) done initialization
|
|
60
|
+
0
|
|
61
|
+
0.9
|
|
62
|
+
1.9
|
|
63
|
+
2.9
|
|
64
|
+
335705:25:16.449 (Roby) received interruption request
|
|
65
|
+
335705:25:16.524 (Roby) control quitting. Waiting for 1 tasks to finish (1 tasks still in plan)
|
|
66
|
+
|
|
67
|
+
== Broken down explanation
|
|
68
|
+
* the line
|
|
69
|
+
State.pos.x = 0
|
|
70
|
+
initializes the robot's state. In general, it is done in the robot's
|
|
71
|
+
configuration file, config/goForward.rb (see below)
|
|
72
|
+
* the line
|
|
73
|
+
arguments :speed
|
|
74
|
+
in the task model tells Roby that the GoForward tasks require a 'speed'
|
|
75
|
+
argument. If it is omitted, the task has no means to actually perform its
|
|
76
|
+
action (it does not know at what speed it is supposed to move), and therefore
|
|
77
|
+
cannot be started. Replace
|
|
78
|
+
Roby.plan.insert(go = GoForward.new(:speed => 0.1))
|
|
79
|
+
by
|
|
80
|
+
Roby.plan.insert(go = GoForward.new)
|
|
81
|
+
and you'll get
|
|
82
|
+
|
|
83
|
+
Roby::EventNotExecutable in GoForward{}:0x4854d110[]/start: start! called on GoForward{}:0x4854d110[] which is partially instanciated
|
|
84
|
+
./controllers/goForward.rb:15
|
|
85
|
+
|
|
86
|
+
a <i>partially instanciated</i> task being a task whose all required
|
|
87
|
+
arguments are not set.
|
|
88
|
+
|
|
89
|
+
* to understand the meaning of the +poll+ statement, you have to understand
|
|
90
|
+
the idea behind Roby's execution model. Roby relies on a _synchronous_
|
|
91
|
+
execution model, which is basically a two-steps loops (a more detailed
|
|
92
|
+
explanation will come in the following tutorials). This two-steps loops
|
|
93
|
+
is basically:
|
|
94
|
+
1. gather all events that have occured since the last loop
|
|
95
|
+
2. react to those events
|
|
96
|
+
|
|
97
|
+
What should be noted here is that the duration of this whole execution loop
|
|
98
|
+
is also a higher bound for the plan-based reaction to new situations. In
|
|
99
|
+
other words, it means that the worst-case latency between the moment
|
|
100
|
+
something happens and the moment the system reacts to it is the duration of
|
|
101
|
+
the execution cycle. In general, one considers that the duration of the
|
|
102
|
+
execution cycle should be small with respect to the system's dynamic (the
|
|
103
|
+
latency in reaction must not have a physical effect).
|
|
104
|
+
|
|
105
|
+
Now, what is the role of +poll+ here ? The block given to +poll+ is executed
|
|
106
|
+
at each execution cycle <i>while the task is running</i>. It can therefore be
|
|
107
|
+
used to break done lengthy computation in small steps, or represent a computation
|
|
108
|
+
thread in the plan, using a task (Roby::PlannerTask does this to represent a plan
|
|
109
|
+
generation thread).
|
|
110
|
+
|
|
111
|
+
* we did not specify a robot name in the call to <tt>scripts/run</tt>. In that
|
|
112
|
+
case, Roby instantiated a robot named 'goForward' of type 'goForward'
|
|
113
|
+
* in general, one does not want the robot to start moving just after
|
|
114
|
+
initialization. To have an interactive interface to the robot's actions, you
|
|
115
|
+
can use the <tt>scripts/shell</tt> tool. See below.
|
|
116
|
+
|
|
117
|
+
= Refining the goForward controller
|
|
118
|
+
== File loading at startup and configuration files
|
|
119
|
+
Roby loads many files at startup, whose exact set of files is determined by the
|
|
120
|
+
robot name and type. The general rule is that the files are loaded from the
|
|
121
|
+
least specific ones (i.e. the files common to all robots) to the most specific ones
|
|
122
|
+
(i.e. the files that are specific to a given robot name).
|
|
123
|
+
|
|
124
|
+
In this tutorial, the <i>task model</i> is global and will be loaded in all
|
|
125
|
+
controllers of this Roby application. The controller file, on the other hand,
|
|
126
|
+
is defined for the goForward robot.
|
|
127
|
+
|
|
128
|
+
What we want here is move the state initialization from the controller file
|
|
129
|
+
into the robot's configuration file. To do that, you just have to move the
|
|
130
|
+
corresponding line into <tt>config/$NAME.rb</tt>, which is in our case
|
|
131
|
+
<tt>config/goForward.rb</tt>, so that this latter file looks like
|
|
132
|
+
|
|
133
|
+
Roby::State.update do |s|
|
|
134
|
+
# define the original value of x
|
|
135
|
+
s.pox.x = 0
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
and test that everything still works !.
|
|
139
|
+
|
|
140
|
+
See Roby::Application for an explanation of how files are organized in a Roby
|
|
141
|
+
application.
|
|
142
|
+
|
|
143
|
+
== Interacting with the Roby controller
|
|
144
|
+
First, we usually don't want to hardcode the robot actions in its controller.
|
|
145
|
+
Instead, it is better to be able to <em>send a command</em> to the robot. Do
|
|
146
|
+
do that, we must first define an <em>action</em> in the robot's main planner.
|
|
147
|
+
Edit <tt>planners/goForward/main.rb</tt> and add the following code to the
|
|
148
|
+
definition of the MainPlanner class.
|
|
149
|
+
|
|
150
|
+
method(:move) do
|
|
151
|
+
GoForward.new :speed => arguments[:speed]
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
and remove the last three lines of controllers/goForward.rb. You can now start
|
|
155
|
+
the application and wait for the "done initialization" line.
|
|
156
|
+
|
|
157
|
+
$ scripts/run goForward
|
|
158
|
+
335814:29:25.107 (goForward) loading controller file /home/doudou/dev/roby-tutorials/controllers/goForward.rb
|
|
159
|
+
335814:29:25.108 (goForward) done initialization
|
|
160
|
+
0
|
|
161
|
+
0
|
|
162
|
+
|
|
163
|
+
Now, start a shell and wait its prompt.
|
|
164
|
+
|
|
165
|
+
$ scripts/shell
|
|
166
|
+
>>
|
|
167
|
+
|
|
168
|
+
Let's now check that the <tt>move</tt> action does exist
|
|
169
|
+
>> actions
|
|
170
|
+
=> [move]
|
|
171
|
+
|
|
172
|
+
... and start the move
|
|
173
|
+
>> m = move! :speed => 0.2
|
|
174
|
+
=> GoForward{speed => 0.2}:0x4886b248[]
|
|
175
|
+
|
|
176
|
+
>> m.running?
|
|
177
|
+
=> true
|
|
178
|
+
>> running_tasks
|
|
179
|
+
=>
|
|
180
|
+
|
|
181
|
+
Task Since State
|
|
182
|
+
GoForward{speed => 0.2}:0x4886b248[] Wed Apr 23 08:31:29 +0200 2008 running
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
Now, to stop it ...
|
|
186
|
+
>> m.stop!
|
|
187
|
+
=> []
|
|
188
|
+
task GoForward{speed => 0.2}:0x4886b248[] stopped by user request
|
|
189
|
+
>> m.running?
|
|
190
|
+
=> false
|
|
191
|
+
>> m.finished?
|
|
192
|
+
=> true
|
|
193
|
+
>> m.success?
|
|
194
|
+
=> false
|
|
195
|
+
>> m.failed?
|
|
196
|
+
=> true
|
|
197
|
+
|
|
198
|
+
The task has been interrupted. From the system point of view, it means that it
|
|
199
|
+
has not finished successfully, hence <tt>m.success?</tt> and <tt>m.failed?</tt>
|
|
200
|
+
return respectively false and true.
|
|
201
|
+
|
|
202
|
+
Now, to make the whole Roby controller quit:
|
|
203
|
+
>> quit
|
|
204
|
+
=>
|
|
205
|
+
|
|
206
|
+
Note that you don't have to restart the shell between to runs: if you start the
|
|
207
|
+
controller again, the same shell will reconnect automatically to the new
|
|
208
|
+
controller.
|
|
209
|
+
|
|
210
|
+
= Next tutorial
|
|
211
|
+
|
|
212
|
+
This tutorial showed you how to build a very simple task model, and how to
|
|
213
|
+
create planner methods to interface with the shell. The {next
|
|
214
|
+
tutorial}[link:files/doc/tutorials/03-PlannedPath_rdoc.html] will build upon
|
|
215
|
+
that by making you create a very small plan, in which different tasks represent
|
|
216
|
+
different aspects of the robot activity. The fourth tutorial will then be about
|
|
217
|
+
displaying the execution trace of that plan to understand what happens under the
|
|
218
|
+
hood.
|
|
219
|
+
---
|
|
220
|
+
vim: tw=80 et
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
{Previous tutorial}[link:files/doc/tutorials/02-GoForward_rdoc.html]
|
|
2
|
+
{Next tutorial}[link:files/doc/tutorials/04-EventPropagation_rdoc.html]
|
|
3
|
+
= Planning and following a path
|
|
4
|
+
We'll now use a (slightly) more complex system to make our robot move. The
|
|
5
|
+
robot will now have a goal, defined as a (x, y) point. It will generate a
|
|
6
|
+
trajectory which leads it to that goal, and then execute that trajectory.
|
|
7
|
+
|
|
8
|
+
This tutorial therefore shows the following:
|
|
9
|
+
* how multiple activities can be _temporally_ coordinated to make the robot
|
|
10
|
+
reach a defined goal, and
|
|
11
|
+
* how the plan represents how one activity relates to another.
|
|
12
|
+
|
|
13
|
+
In this new robot, three activities will be used to make the robot reach
|
|
14
|
+
its goal. The plan will therefore represent various things:
|
|
15
|
+
* the three activities: the high-level activity which represent the goal of
|
|
16
|
+
the robot; the path planning activity and the path execution activity.
|
|
17
|
+
* how these activities relate to each other. For that, Roby defines <it>
|
|
18
|
+
task relations</it>.
|
|
19
|
+
* how the plan describes the temporal relations between these activities (i.e.
|
|
20
|
+
when a given activity should be started).
|
|
21
|
+
|
|
22
|
+
To hold all these, we will create a new robot:
|
|
23
|
+
|
|
24
|
+
roby robot PathPlan
|
|
25
|
+
|
|
26
|
+
== Defining the task models
|
|
27
|
+
This section will describe the task models, without the actual implementation
|
|
28
|
+
of the actual implementation of these activities. That implementation is
|
|
29
|
+
discussed later in that tutorial. The goal is to first make you grasp what the
|
|
30
|
+
task models, and the plan model is about and only then how the tasks can
|
|
31
|
+
actually control the robot itself.
|
|
32
|
+
|
|
33
|
+
* the +MoveTo+ task express the current goal of the robot, and holds the path
|
|
34
|
+
data. Open <tt>tasks/move_to.rb</tt> and add the following:
|
|
35
|
+
class MoveTo < Roby::Task
|
|
36
|
+
terminates
|
|
37
|
+
|
|
38
|
+
# The movement goal
|
|
39
|
+
argument :goal
|
|
40
|
+
# The generated path
|
|
41
|
+
def path; data end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
* the +ComputePath+ task generates the path on behalf of a +MoveTo+. When
|
|
45
|
+
successful, it updates the +data+ attribute of the +MoveTo+ task it is
|
|
46
|
+
planning. It uses a standard task, Roby::ThreadTask, which allows to
|
|
47
|
+
represent the execution of a separate thread into the main plan. Open
|
|
48
|
+
<tt>tasks/compute_path.rb</tt> and add the following:
|
|
49
|
+
|
|
50
|
+
require 'roby/thread_task'
|
|
51
|
+
class ComputePath < Roby::ThreadTask
|
|
52
|
+
# The movement goal
|
|
53
|
+
argument :goal
|
|
54
|
+
# The maximum speed limit
|
|
55
|
+
argument :max_speed
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
* finally, +TrackPath+ takes the path generated and follows it. Open
|
|
59
|
+
<tt>tasks/track_path.rb</tt> and add the following:
|
|
60
|
+
class TrackPath < Roby::Task
|
|
61
|
+
terminates
|
|
62
|
+
|
|
63
|
+
# The task holding the path data
|
|
64
|
+
argument :path_task
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
*Note*: the file names are "best practice" recommandations. They are not at all
|
|
68
|
+
required for the application to work.
|
|
69
|
+
|
|
70
|
+
== Building the movement plan
|
|
71
|
+
Let's add a +move_to+ action to our robot, which builds the plan corresponding
|
|
72
|
+
to the whole movement. The action definition, in
|
|
73
|
+
<tt>planners/PathPlan/main.rb</tt> would look like this:
|
|
74
|
+
|
|
75
|
+
# Note: the method arguments are accessed through the +arguments+ hash
|
|
76
|
+
method(:move_to) do
|
|
77
|
+
# The goal point
|
|
78
|
+
goal = Pos::Vector3D.new(*arguments.values_at(:x, :y))
|
|
79
|
+
# The high-level representation of the movement
|
|
80
|
+
move = MoveTo.new :goal => goal
|
|
81
|
+
move.realized_by compute = ComputePath.new(:goal => goal, :max_speed => 1.0)
|
|
82
|
+
move.realized_by track = TrackPath.new(:path_task => move)
|
|
83
|
+
|
|
84
|
+
move.on :start, compute, :start
|
|
85
|
+
compute.on :success, track, :start
|
|
86
|
+
track.forward :success, move, :success
|
|
87
|
+
|
|
88
|
+
move
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
The first part creates the <em>task structure</em>, which expresses the
|
|
92
|
+
relationships of the different tasks of the plan. This simple plan uses only
|
|
93
|
+
one kind of relation, the RealizedBy relation. In this relation, the child task
|
|
94
|
+
(i.e. +compute+ and +track+) are simple activities which achieve the parent's higher-level
|
|
95
|
+
action.
|
|
96
|
+
|
|
97
|
+
The second part creates the <em>event structure</em>, which expresses how the
|
|
98
|
+
plan should respond to new situations. In our case, the three line describe the following:
|
|
99
|
+
* the path planning must be started when the movement is started. This uses the Signal
|
|
100
|
+
event relation.
|
|
101
|
+
* the path execution must be started when the path planning has successfully finished, and
|
|
102
|
+
* the movement <it>has finished</it> when the path tracking <it>has finished</it>. This uses
|
|
103
|
+
the Forward relation.
|
|
104
|
+
|
|
105
|
+
The difference between those two relations is subtle, so let's try to explain a bit more:
|
|
106
|
+
* in the first two cases, what the system must do is <it>executing a new action</it> in
|
|
107
|
+
response to a new situation. When the +start+ event of +move+ is emitted, the +move+
|
|
108
|
+
activity has just started (i.e. all necessary actions have been taken to start that
|
|
109
|
+
new activity). The system should then make what is necessary to start computing the
|
|
110
|
+
path: it calls the _command_ of the +start+ event of +compute+.
|
|
111
|
+
* in the third case, however, no specific action should be taken to end the
|
|
112
|
+
+move+ task. Instead, the plan expresses that the +move+ task is finished
|
|
113
|
+
<it>as soon as</it> the +track+ task is. Another way to put it is that the
|
|
114
|
+
situation represented by the +success+ event of MoveTo is, in this particular
|
|
115
|
+
plan, the same than the situation represented by the +success+ event of
|
|
116
|
+
TrackPath. More generally, if +a+ is forwarded to +b+ all situations that lead
|
|
117
|
+
to the emission of +a+ also lead to the emission of +b+. Or, in other words, that
|
|
118
|
+
the situation represented by +b+ is a superset of the one represented by +a+
|
|
119
|
+
(the equality, like here, is a particular case)
|
|
120
|
+
|
|
121
|
+
link:../../images/event_generalization.png
|
|
122
|
+
|
|
123
|
+
*Example*: in this plan, the +success+ event of a particular low-level action
|
|
124
|
+
is forwarded in more high-level parts of the plan. This allows to actually
|
|
125
|
+
link the low and high level parts of the plan and reason on that link.
|
|
126
|
+
|
|
127
|
+
Task relations allow the system to keep track of what a given task is useful for,
|
|
128
|
+
what are error conditions and how to react to errors. The next two tutorials will
|
|
129
|
+
describe these parts in more details.
|
|
130
|
+
|
|
131
|
+
== Running this unfinished controller
|
|
132
|
+
|
|
133
|
+
Let's run this controller. Launch the controller
|
|
134
|
+
$ scripts/run PathPlan
|
|
135
|
+
|
|
136
|
+
In another terminal, launch the shell and start the move_to! action
|
|
137
|
+
$ scripts/shell
|
|
138
|
+
>> move_to! :x => 10, :y => 10
|
|
139
|
+
=> MoveTo{goal => Vector3D(x=10.000000,y=10.000000,z=0.000000)}:0x48350370[]
|
|
140
|
+
>>
|
|
141
|
+
!Roby::ChildFailedError
|
|
142
|
+
!at [336040:01:45.419/186] in the failed event of ComputePath:0x483502e0
|
|
143
|
+
!block not supplied (ArgumentError)
|
|
144
|
+
! /home/doudou/dev/roby/lib/roby/thread_task.rb:51:in `instance_eval',
|
|
145
|
+
! /home/doudou/dev/roby/lib/roby/thread_task.rb:61:in `value',
|
|
146
|
+
! /home/doudou/dev/roby/lib/roby/thread_task.rb:61:in the polling handler,
|
|
147
|
+
! /home/doudou/system/powerpc-linux/ruby-1.8.6/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require',
|
|
148
|
+
! /home/doudou/system/powerpc-linux/ruby-1.8.6/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `require',
|
|
149
|
+
! scripts/run:3
|
|
150
|
+
!
|
|
151
|
+
!The failed relation is
|
|
152
|
+
! MoveTo:0x48350370
|
|
153
|
+
! owners: Roby::Distributed
|
|
154
|
+
! arguments: {:goal=>Vector3D(x=10.000000,y=10.000000,z=0.000000)}
|
|
155
|
+
! realized_by ComputePath:0x483502e0
|
|
156
|
+
! owners: Roby::Distributed
|
|
157
|
+
! arguments: {:max_speed=>1.0,
|
|
158
|
+
! :goal=>Vector3D(x=10.000000,y=10.000000,z=0.000000)}
|
|
159
|
+
!The following tasks have been killed:
|
|
160
|
+
! ComputePath:0x483502e0
|
|
161
|
+
! MoveTo:0x48350370
|
|
162
|
+
|
|
163
|
+
Mmmm... What happened ? The call to <tt>move_to!</tt> returned properly, which
|
|
164
|
+
means that the plan has been properly generated and the MoveTo high-level
|
|
165
|
+
action started. Nonetheless, an error occured.
|
|
166
|
+
|
|
167
|
+
The error message appeared because an ArgumentError exception has been raised
|
|
168
|
+
in <tt>thread_task.rb:51</tt> Looking at the documentation of Roby::ThreadTask,
|
|
169
|
+
we see that the definition of ComputePath has not called the Roby::ThreadTask.implementation
|
|
170
|
+
statement, and as such the polling handler failed. Roby answers to that by
|
|
171
|
+
emitting the +failed+ event of the problematic task.
|
|
172
|
+
|
|
173
|
+
The plan-related error (ChildFailedError) has then been generated by Roby's
|
|
174
|
+
plan analysis:
|
|
175
|
+
* a +realized_by+ relation between MoveTo and ComputePath exists, which means
|
|
176
|
+
that MoveTo cannot be achieved without executing ComputePath first.
|
|
177
|
+
* ComputePath failed, so <i>in the current state of the plan</i>, the MoveTo
|
|
178
|
+
action cannot be achieved either.
|
|
179
|
+
|
|
180
|
+
A more complete description of errors and, more importantly, of how to handle
|
|
181
|
+
them is given in the following tutorials.
|
|
182
|
+
|
|
183
|
+
== Implementation of +ComputePath+ and +TrackPath+
|
|
184
|
+
The first section did mainly explain how the plan represents the logical
|
|
185
|
+
relations between each tasks and each task's events. We will now get into the
|
|
186
|
+
details of actually implementing these tasks.
|
|
187
|
+
|
|
188
|
+
* First, we have to initialize the position in <tt>tasks/PathPlan.rb</tt>
|
|
189
|
+
|
|
190
|
+
Roby::State.update do |s|
|
|
191
|
+
s.pos = Roby::Pos::Vector3D.new
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
* for +ComputePath+, we will simply generate a random set of points in-between
|
|
195
|
+
the current robot position and the specified goal. In general (i.e. not here,
|
|
196
|
+
but in a real case), this process takes time and as such cannot be done in
|
|
197
|
+
one pass of the execution cycle. We will therefore use a thread to do it,
|
|
198
|
+
leaving the actual thread management to Roby::ThreadTask:
|
|
199
|
+
|
|
200
|
+
# The robot position at which we should start planning
|
|
201
|
+
# the path
|
|
202
|
+
attr_reader :start_point
|
|
203
|
+
|
|
204
|
+
# Initialize start_point and call ThreadTask's start command
|
|
205
|
+
event :start do |context|
|
|
206
|
+
@start_point = State.pos.dup
|
|
207
|
+
super
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# Implementation of the computation thread
|
|
211
|
+
implementation do
|
|
212
|
+
path = [start_point]
|
|
213
|
+
while goal.distance(path.last) > max_speed
|
|
214
|
+
u = goal - path.last
|
|
215
|
+
u /= u.length / max_speed
|
|
216
|
+
path << path.last + u
|
|
217
|
+
end
|
|
218
|
+
path << goal
|
|
219
|
+
|
|
220
|
+
Robot.info "#{path.size} points between #{start_point} and #{goal}"
|
|
221
|
+
path
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
on :success do |ev|
|
|
225
|
+
# Parents is a ValuSet, it has no #first method. Get
|
|
226
|
+
# the first element with #find
|
|
227
|
+
parents.find { true }.data = result
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
See Roby::ThreadTask to implement _interruptible_ external threads.
|
|
231
|
+
|
|
232
|
+
Robot is a namespace which (among other things) can be used to access an
|
|
233
|
+
application-specific logger set up by Roby itself. It answers to #debug,
|
|
234
|
+
#info, #warning and #fatal, and by default is at the INFO level. The Logger
|
|
235
|
+
object itself is accessible at Robot.logger. Therefore, use
|
|
236
|
+
Robot.logger.level= to change the logger level itself.
|
|
237
|
+
|
|
238
|
+
* as stated before, MoveTo does not require any special code. It is here
|
|
239
|
+
only to represent a high level activity (the whole movement), not to actually
|
|
240
|
+
execute it.
|
|
241
|
+
|
|
242
|
+
* TrackPath will then take the path data and execute the corresponding movement. For
|
|
243
|
+
the purpose of this tutorial, it will simply move to the next point in the path
|
|
244
|
+
at each execution cycle:
|
|
245
|
+
|
|
246
|
+
# The current waypoint
|
|
247
|
+
def current_waypoint; path_task.data[@waypoint_index] end
|
|
248
|
+
|
|
249
|
+
poll do
|
|
250
|
+
@waypoint_index ||= 0
|
|
251
|
+
State.pos = current_waypoint
|
|
252
|
+
@waypoint_index += 1
|
|
253
|
+
if @waypoint_index == path_task.data.size
|
|
254
|
+
emit :success
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
Robot.info "moved to #{current_waypoint}"
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
= Next tutorial
|
|
261
|
+
|
|
262
|
+
The {next tutorial}[link:files/doc/tutorials/04-EventPropagation_rdoc.html] will
|
|
263
|
+
allow you to understand more by actually seeing what happens during the plan
|
|
264
|
+
execution. After this tutorial, you should be able to build simple task
|
|
265
|
+
models and simple plans, as well as execute them and understand the most common
|
|
266
|
+
error -- ChildFailedError.
|
|
267
|
+
---
|
|
268
|
+
vim: tw=80 et
|