|
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