ruby-player 0.0.1 → 0.1.0

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.
@@ -0,0 +1,59 @@
1
+ # Ruby Player - Ruby client library for Player (tools for robots)
2
+ #
3
+ # Copyright (C) 2012 Timin Aleksey
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+
15
+ module Player
16
+ class DevAddr
17
+ include Common
18
+
19
+ attr_reader :host, :robot, :interface, :index
20
+
21
+ def initialize(addr = {})
22
+ @host = addr[:host].to_i
23
+ @robot = addr[:robot].to_i
24
+ @interface = addr[:interface].to_i
25
+ @index = addr[:index].to_i
26
+ end
27
+
28
+ def DevAddr.decode(str)
29
+ ary = str.unpack("NNNN")
30
+ DevAddr.new(host: ary[0], robot: ary[1], interface: ary[2], index: ary[3])
31
+ end
32
+
33
+ def encode
34
+ [@host, @robot, @interface, @index].pack("NNNN")
35
+ end
36
+
37
+ def interface_name
38
+ @interface_name ||= search_interface_name(@interface)
39
+ end
40
+
41
+ def ==(other)
42
+ @host == other.host and @robot == other.robot and @interface == other.interface and @index == other.index
43
+ end
44
+
45
+ def to_s
46
+ "device addr [#@host:#@robot] #@interface_name:#@index"
47
+ end
48
+
49
+ def to_a
50
+ [@host, @robot, @interface, @index]
51
+ end
52
+
53
+ private
54
+ def search_interface_name(code)
55
+ name = search_const_name code, /PLAYER_[\w]*_CODE/
56
+ name.to_s.scan(/PLAYER_([\w]*)_CODE/).join.downcase
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,60 @@
1
+ # Ruby Player - Ruby client library for Player (tools for robots)
2
+ #
3
+ # Copyright (C) 2012 Timin Aleksey
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ require "socket"
15
+
16
+ module Player
17
+ class Device
18
+ include Common
19
+
20
+ # Device address
21
+ attr_reader :addr
22
+
23
+ # Device geometry
24
+ # @return [Hash] geometry { :px, :py. :pz, :proll, :ppitch, :pyaw, :sw, :sl, :sh }
25
+ attr_reader :geom
26
+
27
+
28
+ def initialize(addr, client, log_level)
29
+ @addr, @client = addr, client
30
+ @log_level = log_level
31
+
32
+ @geom = {px: 0.0, py: 0.0, pz: 0.0, proll: 0.0, ppitch: 0.0, pyaw: 0.0, sw: 0.0, sl: 0.0, sh: 0.0}
33
+ end
34
+
35
+ def fill(hdr,msg)
36
+ raise_error "Method `fill` isn't implemented for `#{self.class}`"
37
+ end
38
+
39
+ def handle_response(hdr, msg)
40
+ raise_error "Method `handle_response` isn't implemented for `#{self.class}`"
41
+ end
42
+
43
+ private
44
+ def send_message(type, subtype, msg="")
45
+ @client.write( Header.new(
46
+ dev_addr: @addr,
47
+ type: type,
48
+ subtype: subtype,
49
+ size: msg.bytesize), msg)
50
+ end
51
+
52
+ def read_geom(msg)
53
+ data = msg.unpack("G*")
54
+ [:px,:py,:pz, :proll,:ppitch,:pyaw, :sw,:sl,:sh].each_with_index do |k,i|
55
+ @geom[k] = data[i]
56
+ end
57
+ debug("Get geom px=%.2f py=%.2f pz=%.2f; proll=%.2f, ppitch=%.2f, pyaw=%.2f, sw=%.2f, sl=%.2f, sh=%.2f" % @geom.values)
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,81 @@
1
+ # Ruby Player - Ruby client library for Player (tools for robots)
2
+ #
3
+ # Copyright (C) 2012 Timin Aleksey
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+
15
+ module Player
16
+ class Header
17
+ include Common
18
+
19
+ attr_reader :dev_addr, :type, :subtype, :time, :seq, :size
20
+
21
+ def initialize(param = {})
22
+ @dev_addr = param[:dev_addr]
23
+ @type = param[:type].to_i
24
+ @subtype = param[:subtype].to_i
25
+ @time = param[:time] || Time.now.to_f / 1000
26
+ @seq = param[:seq].to_i
27
+ @size = param[:size].to_i
28
+
29
+ end
30
+
31
+ def Header.decode(str)
32
+ dev_addr = DevAddr.decode(str[0,16])
33
+ type, subtype, time, seq, size = str[16,24].unpack("NNGNN")
34
+ Header.new(dev_addr: dev_addr, type: type, subtype: subtype, time: time, seq: seq, size: size)
35
+ end
36
+
37
+ def Header.from_a(ary)
38
+ Header.decode ary.pack("NNNNNNGNN")
39
+ end
40
+
41
+ def encode
42
+ @dev_addr.encode + [@type, @subtype, @time, @seq, @size].pack("NNGNN")
43
+ end
44
+
45
+ def ==(other)
46
+ @dev_addr == other.dev_addr and @type == other.type and @subtype == other.subtype and @time == other.time and @seq == other.seq and @size == other.size
47
+ end
48
+
49
+ def to_s
50
+ "header to #@dev_addr of message [type=#{type_name}, subtype=#{subtype_name}, size=#@size]"
51
+ end
52
+
53
+ def type_name
54
+ @type_name ||= search_msg_type_name(@type)
55
+ end
56
+
57
+ def subtype_name
58
+ @subtype_name ||= search_msg_subtype_name(dev_addr.interface_name, type_name, @subtype)
59
+ end
60
+
61
+ private
62
+ def search_msg_type_name(type)
63
+ name = search_const_name type, /PLAYER_MSGTYPE_[\w]*/
64
+ if name != ""
65
+ name[7..-1]
66
+ else
67
+ type.to_s
68
+ end
69
+ end
70
+
71
+ def search_msg_subtype_name(interface_name, type_name, subtype)
72
+ type_prefix = type_name == "MSGTYPE_RESP_ACK" ? "" : type_name.split("_")[-1]
73
+ name = search_const_name subtype, "PLAYER_" + interface_name.upcase + "_" + type_prefix + ".*"
74
+ if name != ""
75
+ name[7..-1]
76
+ else
77
+ subtype.to_s
78
+ end
79
+ end
80
+ end
81
+ end
@@ -17,146 +17,192 @@ module Player
17
17
  #
18
18
  # @example
19
19
  # # get proxy object
20
- # pos2d = client[:position2d, 0]
20
+ # pos2d = client.subcribe("position2d", index: 0)
21
21
  # # setup speed of robot
22
22
  # pos2d.set_speed(vx: 1.2, vy: 0.1, va: 0.3)
23
23
  #
24
24
  # #update data from server
25
- # client.read
25
+ # client.read!
26
26
  # #read velocityand position by X,Y and angle
27
- # pos2d.speed #=> { :vx => 1.2, :vy => 0.1, :va => 0.3 }
28
- # pos2d.odometry #=> { :px => 0.2321, :py => 0,01, :pa => 0.2 }
29
- # pos2d.stop
30
- class Position2d
31
- include CType
32
- include Common
27
+ # pos2d.position #=> { :px => 0.2321, :py => 0,01, :pa => 0.2, :vx => 1.2, :vy => 0.1, :va => 0.3, :stall => 1 }
28
+ # pos2d.stop!
29
+ class Position2d < Device
33
30
 
34
- module C
35
- extend FFI::Library
36
- ffi_lib "playerc"
31
+ # Position of robot
32
+ # @return [Hash] hash position {:px, :py, :pa, :vx, :vy, :va, :stall }
33
+ attr_reader :position
37
34
 
38
- attach_function :playerc_position2d_create, [:pointer, :int], :pointer
39
- attach_function :playerc_position2d_destroy, [:pointer], :void
40
- attach_function :playerc_position2d_subscribe, [:pointer, :int], :int
41
- attach_function :playerc_position2d_unsubscribe, [:pointer], :int
35
+ def initialize(addr, client, log_level)
36
+ super
37
+ @position = {px: 0.0, py: 0.0, pa: 0.0, vx: 0.0, vy: 0.0, va: 0.0, stall: 0}
38
+ end
42
39
 
43
- attach_function :playerc_position2d_get_geom, [:pointer], :int
44
- attach_function :playerc_position2d_set_cmd_vel, [:pointer, :double, :double, :double, :int], :int
45
- attach_function :playerc_position2d_set_cmd_car, [:pointer, :double, :double], :int
46
- attach_function :playerc_position2d_enable, [:pointer, :int], :int
47
- attach_function :playerc_position2d_set_odom, [:pointer, :double, :double, :double], :int
40
+ # Query robot geometry
41
+ # @return self
42
+ def query_geom
43
+ send_message(PLAYER_MSGTYPE_REQ, PLAYER_POSITION2D_REQ_GET_GEOM)
44
+ self
45
+ end
46
+
47
+ # Turn on motor
48
+ # @return self
49
+ def turn_on!
50
+ send_message(PLAYER_MSGTYPE_REQ, PLAYER_POSITION2D_REQ_MOTOR_POWER, [1].pack("N"))
51
+ self
52
+ end
53
+
54
+ # Turn off motor
55
+ def turn_off!
56
+ send_message(PLAYER_MSGTYPE_REQ, PLAYER_POSITION2D_REQ_MOTOR_POWER, [0].pack("N"))
57
+ self
48
58
  end
49
59
 
50
- def initialize(client, index)
51
- @pos2d = Position2dStruct.new(C.playerc_position2d_create(client, index))
52
- try_with_error C.playerc_position2d_subscribe(@pos2d, PLAYER_OPEN_MODE)
60
+ def direct_speed_control!
61
+ send_message(PLAYER_MSGTYPE_REQ, PLAYER_POSITION2D_REQ_VELOCITY_MODE, [0].pack("N"))
62
+ self
63
+ end
53
64
 
54
- ObjectSpace.define_finalizer(self, Position2d.finilazer(@pos2d))
65
+ def separate_speed_control!
66
+ send_message(PLAYER_MSGTYPE_REQ, PLAYER_POSITION2D_REQ_VELOCITY_MODE, [1].pack("N"))
67
+ self
55
68
  end
56
69
 
57
- # Odometry of robot
58
- # @return [Hash] hash odometry {:px, :py, :pa }
59
- def odometry
60
- {
61
- px: @pos2d[:px],
62
- py: @pos2d[:py],
63
- pa: @pos2d[:pa]
64
- }
70
+ def position_control!
71
+ send_message(PLAYER_MSGTYPE_REQ, PLAYER_POSITION2D_REQ_POSITION_MODE, [0].pack("N"))
72
+ self
65
73
  end
66
-
74
+
75
+ def speed_control!
76
+ send_message(PLAYER_MSGTYPE_REQ, PLAYER_POSITION2D_REQ_POSITION_MODE, [1].pack("N"))
77
+ self
78
+ end
79
+
67
80
  # Set odometry of robot.
68
- # @params [Hash] odometry
81
+ # @param [Hash] odometry
69
82
  # @option odometry :px x position (m)
70
83
  # @option odometry :py y position (m)
71
84
  # @option odometry :pa angle (rad).
72
85
  # @return self
73
- def set_odometry(odometry)
74
- args = [
75
- odometry[:px].to_f || @pos2d[:px],
76
- odometry[:py].to_f || @pos2d[:py],
77
- odometry[:pa].to_f || @pos2d[:pa]
86
+ def set_odometry(odom)
87
+ data = [
88
+ odom[:px].to_f,
89
+ odom[:py].to_f,
90
+ odom[:pa].to_f
78
91
  ]
92
+
93
+ send_message(PLAYER_MSGTYPE_REQ, PLAYER_POSITION2D_REQ_SET_ODOM, data.pack("GGG"))
94
+ self
95
+ end
79
96
 
80
- try_with_error C.playerc_position2d_set_odom(@pos2d, *args)
97
+ # Set speed PID paramters
98
+ # @param [Hash] params PID params
99
+ # @option params :kp P
100
+ # @option params :ki I
101
+ # @option params :kd D
102
+ # @return self
103
+ def set_speed_pid(params={})
104
+ data = [
105
+ params[:kp].to_f,
106
+ params[:ki].to_f,
107
+ params[:kd].to_f
108
+ ]
109
+ send_message(PLAYER_MSGTYPE_REQ, PLAYER_POSITION2D_REQ_SPEED_PID, data.pack("GGG"))
81
110
  self
82
111
  end
83
112
 
84
- # Reset odometry to zero
113
+ # Set position PID paramters
114
+ # @param [Hash] params PID params
115
+ # @option params :kp P
116
+ # @option params :ki I
117
+ # @option params :kd D
85
118
  # @return self
86
- def reset_odometry
87
- set_odometry(px: 0, py: 0, pa: 0)
119
+ def set_position_pid(params={})
120
+ data = [
121
+ params[:kp].to_f,
122
+ params[:ki].to_f,
123
+ params[:kd].to_f
124
+ ]
125
+ send_message(PLAYER_MSGTYPE_REQ, PLAYER_POSITION2D_REQ_POSITION_PID, data.pack("GGG"))
126
+ self
88
127
  end
89
128
 
90
- # Robot geometry
91
- # @return [Hash] position :px, :py, :pa, size :sx, :sy
92
- def geom
93
- try_with_error C.playerc_position2d_get_geom(@pos2d)
94
- p = @pos2d[:pose].to_a
95
- s = @pos2d[:size].to_a
96
- {
97
- px: p[0],
98
- py: p[1],
99
- pa: p[2],
100
- sx: s[0],
101
- sy: s[1]
102
- }
129
+ # Set speed profile
130
+ # @param [Hash] params profile prarams
131
+ # @option params :spped max speed (m/s)
132
+ # @option params :acc max acceleration (m/s^2)
133
+ # @return self
134
+ def set_speed_profile(params={})
135
+ data = [
136
+ params[:speed].to_f,
137
+ params[:acc].to_f
138
+ ]
139
+
140
+ send_message(PLAYER_MSGTYPE_REQ, PLAYER_POSITION2D_REQ_SPEED_PROF, data.pack("GG"))
141
+ self
103
142
  end
104
143
 
144
+ # Reset odometry to zero
145
+ # @return self
146
+ def reset_odometry
147
+ send_message(PLAYER_MSGTYPE_REQ, PLAYER_POSITION2D_REQ_RESET_ODOM)
148
+ self
149
+ end
105
150
 
106
151
  # Set speed of robot. All speeds are defined in the robot coordinate system.
107
- # @params [Hash] speeds
152
+ # @param [Hash] speeds
108
153
  # @option speeds :vx forward speed (m/s)
109
154
  # @option speeds :vy sideways speed (m/s); this field is used by omni-drive robots only.
110
155
  # @option speeds :va rotational speed (rad/s).
156
+ # @option speeds :stall state of motor
111
157
  # @return self
112
158
  def set_speed(speeds)
113
- args = [
114
- speeds[:vx].to_f || @pos2d[:vx],
115
- vy = speeds[:vy].to_f || @pos2d[:vy],
116
- va = speeds[:va].to_f || @pos2d[:va],
117
- 1
159
+ data = [
160
+ speeds[:vx] || @position[:vx],
161
+ speeds[:vy] || @position[:vy],
162
+ speeds[:va] || @position[:va],
163
+ speeds[:stall] || @position[:stall]
118
164
  ]
119
- try_with_error C.playerc_position2d_set_cmd_vel(@pos2d, *args)
165
+ send_message(PLAYER_MSGTYPE_CMD, PLAYER_POSITION2D_CMD_VEL, data.pack("GGGN"))
120
166
  self
121
167
  end
122
168
 
123
169
  # Set speed of carlike robot
124
- # @params [Hash] speeds
170
+ # @param [Hash] speeds
125
171
  # @option speeds :vx forward speed (m/s)
126
- # @option speeds :a angle robot (rad).
172
+ # @option speeds :a turning angle (rad).
127
173
  def set_car(speeds)
128
- args = [
129
- speeds[:vx].to_f || @pos2d[:vx],
130
- speeds[:a].to_f || @pos2d[:pa]
174
+ data = [
175
+ speeds[:vx] || @position[:vx],
176
+ speeds[:a] || @position[:pa]
131
177
  ]
132
- try_with_error C.playerc_position2d_set_cmd_car(@pos2d, *args)
178
+ send_message(PLAYER_MSGTYPE_CMD, PLAYER_POSITION2D_CMD_CAR, data.pack("GG"))
133
179
  self
134
180
  end
135
181
 
136
- # Velocity of robot
137
- # @return [Hash] hash of speeds
138
- def speed
139
- {
140
- vx: @pos2d[:vx],
141
- vy: @pos2d[:vy],
142
- va: @pos2d[:va]
143
- }
182
+ # The position interface accepts translational velocity + absolute heading commands
183
+ # (speed and angular position) for the robot's motors (only supported by some drivers)
184
+ # @param [Hash] speeds
185
+ # @option speeds :vx forward speed (m/s)
186
+ # @option speeds :a absolutle angle (rad).
187
+ def set_speed_head(speeds)
188
+ data = [
189
+ speeds[:vx] || @position[:vx],
190
+ speeds[:a] || @position[:pa]
191
+ ]
192
+ send_message(PLAYER_MSGTYPE_CMD, PLAYER_POSITION2D_CMD_VEL_HEAD, data.pack("GG"))
193
+ self
144
194
  end
145
195
 
196
+
146
197
  # State of motor
147
198
  # @return [Boolean] true - on
148
- def enable
149
- @pos2d[:stall] == 1
150
- end
151
-
152
- # Turn on\off motor
153
- # @param [Boolean] true - turn on
154
- def enable=(val)
155
- try_with_error C.playerc_position2d_enable(@pos2d, val ? 1 : 0)
199
+ def power
200
+ @position[:stall] == 1
156
201
  end
157
202
 
158
203
  # Stop robot set speed to 0
159
- def stop
204
+ # @return self
205
+ def stop!
160
206
  set_speed(vx: 0, vy: 0, va: 0)
161
207
  end
162
208
 
@@ -165,11 +211,35 @@ module Player
165
211
  speed[:vx] == 0 && speed[:vy] == 0 && speed[:va] == 0
166
212
  end
167
213
 
168
- def Position2d.finilazer(pos)
169
- lambda{
170
- try_with_error C.playerc_position2d_unsubscribe(pos)
171
- C.playerc_position2d_destroy(pos)
172
- }
214
+ def fill(hdr, msg)
215
+ case hdr.subtype
216
+ when PLAYER_POSITION2D_DATA_STATE
217
+ read_position(msg)
218
+ when PLAYER_POSITION2D_DATA_GEOM
219
+ read_geom(msg)
220
+ else
221
+ unexpected_message hdr
222
+ end
223
+ end
224
+
225
+ def handle_response(hdr, msg)
226
+ case hdr.subtype
227
+ when PLAYER_POSITION2D_REQ_GET_GEOM
228
+ read_geom(msg)
229
+ when 2..9
230
+ nil #null response
231
+ else
232
+ unexpected_message hdr
233
+ end
234
+ end
235
+
236
+ private
237
+ def read_position(msg)
238
+ data = msg.unpack("GGGGGGN")
239
+ @position.keys.each_with_index do |k,i|
240
+ @position[k] = data[i]
241
+ end
242
+ debug("Get position px=%.2f py=%.2f pa=%.2f; vx=%.2f, vy=%.2f, va=%.2f, stall=%d" % position.values)
173
243
  end
174
244
  end
175
245
  end