|
3 | 3 | [](https://travis-ci.org/jdlangs/RobotOS.jl)
|
4 | 4 | [](https://coveralls.io/r/jdlangs/RobotOS.jl?branch=master)
|
5 | 5 |
|
6 |
| -## Overview |
| 6 | +The Julia client library for [ROS](http://wiki.ros.org/) (Robot Operating System). |
7 | 7 |
|
8 |
| -### Description |
| 8 | +Documentation links: |
9 | 9 |
|
10 |
| -This package enables interfacing Julia code with a ROS ([Robot Operating |
11 |
| -System](http://wiki.ros.org)) system. It works by generating native Julia types |
12 |
| -for ROS types, the same as in C++ or Python, and then wrapping rospy through |
13 |
| -the PyCall package to get communication through topics, services, and |
14 |
| -parameters. |
| 10 | +[](https://jdlangs.github.io/RobotOS.jl/stable) |
15 | 11 |
|
16 |
| -### Installation |
17 |
| - |
18 |
| - Pkg.add("RobotOS") |
19 |
| - using RobotOS |
20 |
| - |
21 |
| -### Contributing |
22 |
| - |
23 |
| -The package will hopefully continue to undergo substantial improvement. Please |
24 |
| -feel free to submit either an issue or pull request through github if you want |
25 |
| -to fix something or suggest a needed improvment, even if it's just to add an |
26 |
| -extra sentence in this README. |
27 |
| - |
28 |
| -#### Testing |
29 |
| - |
30 |
| -Currently, `Pkg.test("RobotOS")` requires some bootstrapping to work properly. |
31 |
| -Before running Julia, make sure a ROS master is running and start the helper |
32 |
| -node by running the `test/echonode.py` file. |
33 |
| - |
34 |
| -## Usage: Type Generation |
35 |
| - |
36 |
| -ROS types are brought into your program with the `@rosimport` macro which |
37 |
| -specifies a package and one or more types. The three valid syntax forms can be |
38 |
| -seen in these examples: |
39 |
| - |
40 |
| - @rosimport std_msgs.msg.Header |
41 |
| - @rosimport nav_msgs.srv: GetPlan |
42 |
| - @rosimport geometry_msgs.msg: PoseStamped, Vector3 |
43 |
| - |
44 |
| -`@rosimport` will import the python modules for the requested type and all |
45 |
| -its dependencies but the native Julia types are not created yet since any |
46 |
| -inter-module dependencies have to be resolved first. After the final |
47 |
| -`@rosimport` call, initiate the type generation with: |
48 |
| - |
49 |
| - rostypegen() |
50 |
| - |
51 |
| -The new types will be placed in newly created modules in `Main`, corresponding |
52 |
| -to the packages requested. For example, `"std_msgs/Header" => |
53 |
| -std_msgs.msg.Header`. After calling `rostypegen()` they can be interacted with |
54 |
| -just like regular modules with `import` and `using` statements bringing the |
55 |
| -generated type names into the local namespace. |
56 |
| - |
57 |
| - using nav_msgs.msg |
58 |
| - import geometry_msgs.msg: Pose, Vector3 |
59 |
| - p = Path() |
60 |
| - v = Vector3(1.1,2.2,3.3) |
61 |
| - |
62 |
| -There is one special case, where the ROS type name conflicts with a built-in |
63 |
| -Julia type name (e.g., `std_msgs/Float64` or `std_msgs/String`). In these |
64 |
| -cases, the generated Julia type will have "Msg" appended to the name for |
65 |
| -disambiguation (e.g., `std_msgs.msg.Float64Msg` and `std_msgs.msg.StringMsg`). |
66 |
| - |
67 |
| -An additional function, `rostypereset()`, resets the type generation process, |
68 |
| -possibly useful for development in the REPL. When invoked, new `@rosimport` |
69 |
| -calls will be needed to generate the same or different types, and previously |
70 |
| -generated modules will be overwritten after `rostypegen()` is called again. Keep |
71 |
| -in mind that names cannot be cleared once defined so if a module is not |
72 |
| -regenerated, the first version will remain. |
73 |
| - |
74 |
| -## Usage: ROS API |
75 |
| - |
76 |
| -In general, the API functions provided directly match those provided in rospy, |
77 |
| -with few cosmetic differences. The rospy API functions can reviewed here: |
78 |
| -[http://wiki.ros.org/rospy/Overview](http://wiki.ros.org/rospy/Overview) |
79 |
| - |
80 |
| -### General Functions |
81 |
| - |
82 |
| -- `init_node(name::String; kwargs...)` : Initialize node. Passes keyword |
83 |
| -arguments on to rospy directly. (Required) |
84 |
| -- `is_shutdown()` : Check for ROS shutdown state. |
85 |
| -- `spin()` : Wait for callbacks until shutdown happens. |
86 |
| -- `logdebug`,`loginfo`,`logwarn`,`logerr`,`logfatal` all work as in rospy. |
87 |
| - |
88 |
| -### Time |
89 |
| - |
90 |
| -Native Julia types `Time` and `Duration` are defined, both as a composite of an |
91 |
| -integral number of seconds and nanoseconds, as in rospy. Arithmetic and |
92 |
| -comparison operators are also defined. A `Rate` type is defined as a wrapper |
93 |
| -for the rospy Rate, which keeps loops running on a near fixed time interval. It |
94 |
| -can be constructed with a `Duration` object, or a floating-point value, |
95 |
| -specifying the loop rate in Hz. Other functions are: |
96 |
| - |
97 |
| -- `get_rostime()`, `RobotOS.now()` : Current time as `Time` object. |
98 |
| -- `to_sec(time_obj)`, `convert(Float64, time_obj)` : Convert `Time` or |
99 |
| -`Duration` object to floating-point number of seconds. |
100 |
| -- `to_nsec(time_obj)` : Convert object to integral number of nanoseconds. |
101 |
| -- `rossleep(t)` with `t` of type `Duration`, `Rate`, `Real`. Also |
102 |
| -`sleep(t::Duration)` and `sleep(t::Rate)` : Sleep the amount implied by type |
103 |
| -and value of the `t` parameter. |
104 |
| - |
105 |
| -### Publishing Messages |
106 |
| - |
107 |
| -Publishing messages is the same as in rospy, except use the `publish` method, |
108 |
| -paired with a Publisher object. For example: |
109 |
| - |
110 |
| - using geometry_msgs.msg |
111 |
| - pub = Publisher{PointStamped}("topic", queue_size = 10) #or... |
112 |
| - #pub = Publisher("topic", PointStamped, queue_size = 10) |
113 |
| - msg = PointStamped() |
114 |
| - msg.header.stamp = now() |
115 |
| - msg.point.x = 1.1 |
116 |
| - publish(pub, msg) |
117 |
| - |
118 |
| -The keyword arguments in the `Publisher` constructor are passed directly on to |
119 |
| -rospy so anything it accepts will be valid. |
120 |
| - |
121 |
| -### Subscribing to a Topic |
122 |
| - |
123 |
| -Subscribing to a topic is the same as in rospy. When creating a `Subscriber`, |
124 |
| -an optional `callback_args` parameter can be given to forward on whenever the |
125 |
| -callback is invoked. Note that it must be passed as a tuple, even if there is |
126 |
| -only a single argument. And again, keyword arguments are directly forwarded. An |
127 |
| -example: |
128 |
| - |
129 |
| - using sensor_msgs.msg |
130 |
| - cb1(msg::Imu, a::String) = println(a,": ",msg.linear_acceleration.x) |
131 |
| - cb2(msg::Imu) = println(msg.angular_velocity.z) |
132 |
| - sub1 = Subscriber{Imu}("topic", cb1, ("accel",), queue_size = 10) #or... |
133 |
| - #sub1 = Subscriber("topic", Imu, cb1, ("accel",), queue_size = 10) |
134 |
| - sub2 = Subscriber{Imu}("topic", cb2, queue_size = 10) |
135 |
| - spin() |
136 |
| - |
137 |
| -### Using services |
138 |
| - |
139 |
| -ROS services are fully supported, including automatic request and response type |
140 |
| -generation. For the `@rosimport` call, use the plain service type name. After |
141 |
| -`rostypegen()`, the generated `.srv` submodule will contain 3 types: the plain |
142 |
| -type, a request type, and a response type. For example `@rosimport |
143 |
| -nav_msgs.srv.GetPlan` will create `GetPlan`, `GetPlanRequest`, and |
144 |
| -`GetPlanResponse`. To provide the service to other nodes, you would create a |
145 |
| -`Service{GetPlan}` object. To call it, a `ServiceProxy{GetPlan}` object. The |
146 |
| -syntax exactly matches rospy to construct and use these objects. For example, |
147 |
| -if `myproxy` is a `ServiceProxy` object, it can be called with |
148 |
| -`myproxy(my_request)`. |
149 |
| - |
150 |
| -### Parameter Server |
151 |
| - |
152 |
| -`get_param`, `set_param`, `has_param`, and `delete_param` are all implemented |
153 |
| -in the `RobotOS` module with the same syntax as in rospy. |
154 |
| - |
155 |
| -### Message Constants |
156 |
| -Message constants may be accessed using `getindex` syntax. For example for |
157 |
| -[visualization_msgs/Marker.msg](http://docs.ros.org/api/visualization_msgs/html/msg/Marker.html) |
158 |
| -we have: |
159 |
| - |
160 |
| - import visualization_msgs.msg: Marker |
161 |
| - Marker[:SPHERE] == getindex(Marker, :SPHERE) == 2 # true |
162 |
| - |
163 |
| -## ROS Integration |
164 |
| - |
165 |
| -Since Julia code needs no prior compilation, it is possible to integrate very |
166 |
| -tightly and natively with a larger ROS system. Just make sure you: |
167 |
| - |
168 |
| -- Keep your code inside your ROS packages as usual. |
169 |
| -- Ensure your .jl script is executable (e.g., `chmod a+x script.jl`) and has |
170 |
| -the hint to the Julia binary as the first line (`#!/usr/bin/env julia`). |
171 |
| - |
172 |
| -Now your Julia code will run exactly like any python script that gets invoked |
173 |
| -through `rosrun` or `roslaunch`. And since `include` takes paths relative to |
174 |
| -the location of the calling file, you can bring in whatever other modules or |
175 |
| -functions reside in your package from the single executable script. |
176 |
| - |
177 |
| - #!/usr/bin/env julia |
178 |
| - #main.jl in thebot_pkg/src |
179 |
| - using RobotOS |
180 |
| - |
181 |
| - include("BotSrc/Bot.jl") |
182 |
| - using Bot |
183 |
| - #... |
184 |
| - |
185 |
| -## Full example |
186 |
| - |
187 |
| -This example demonstrates publishing a random `geometry_msgs/Point` message at |
188 |
| -5 Hz. It also listens for incoming `geometry_msgs/Pose2D` messages and |
189 |
| -republishes them as Points. |
190 |
| - |
191 |
| - #!/usr/bin/env julia |
192 |
| - |
193 |
| - using RobotOS |
194 |
| - @rosimport geometry_msgs.msg: Point, Pose2D |
195 |
| - rostypegen() |
196 |
| - using geometry_msgs.msg |
197 |
| - |
198 |
| - function callback(msg::Pose2D, pub_obj::Publisher{Point}) |
199 |
| - pt_msg = Point(msg.x, msg.y, 0.0) |
200 |
| - publish(pub_obj, pt_msg) |
201 |
| - end |
202 |
| - |
203 |
| - function loop(pub_obj) |
204 |
| - loop_rate = Rate(5.0) |
205 |
| - while ! is_shutdown() |
206 |
| - npt = Point(rand(), rand(), 0.0) |
207 |
| - publish(pub_obj, npt) |
208 |
| - rossleep(loop_rate) |
209 |
| - end |
210 |
| - end |
211 |
| - |
212 |
| - function main() |
213 |
| - init_node("rosjl_example") |
214 |
| - pub = Publisher{Point}("pts", queue_size=10) |
215 |
| - sub = Subscriber{Pose2D}("pose", callback, (pub,), queue_size=10) |
216 |
| - loop(pub) |
217 |
| - end |
218 |
| - |
219 |
| - if ! isinteractive() |
220 |
| - main() |
221 |
| - end |
222 |
| - |
223 |
| -## Versions |
224 |
| - |
225 |
| -- `0.1` : Initial release |
226 |
| -- `0.2` : Changed type gen API and moved generated modules to Main |
227 |
| -- `0.3` : Added service type generation and API |
228 |
| -- `0.4` : Julia v0.4+ support only |
| 12 | +[](https://jdlangs.github.io/RobotOS.jl/latest) |
0 commit comments