Skip to content
This repository was archived by the owner on Oct 4, 2023. It is now read-only.

Commit 32b6d17

Browse files
Pyreach sync 20220425
1 parent cd39cae commit 32b6d17

File tree

6 files changed

+117
-20
lines changed

6 files changed

+117
-20
lines changed

pyreach/digital_output.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,20 @@ class DigitalOutputState:
3636
robot_name: str
3737
pin_states: Tuple[DigitalOutputPinState, ...]
3838

39+
def get_pin_state(self, name: str) -> Optional[DigitalOutputPinState]:
40+
"""Get pin state by name.
41+
42+
Args:
43+
name: the name of the pin.
44+
45+
Returns:
46+
The pin, or None if not specified.
47+
"""
48+
for state in self.pin_states:
49+
if state.name == name:
50+
return state
51+
return None
52+
3953

4054
class DigitalOutput:
4155
"""Class for interacting with a digital output."""
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
"""Blink the april-led on and off."""
2+
3+
import time
4+
from typing import List
5+
6+
from absl import app # type: ignore
7+
from absl import flags # type: ignore
8+
from pyreach.factory import ConnectionFactory
9+
10+
11+
def main(unused_argv: List[str]) -> None:
12+
with ConnectionFactory(
13+
connection_string=flags.FLAGS.connection_string,
14+
enable_streaming=False,
15+
robot_types={
16+
"april-led": "april-led.urdf"
17+
}).connect() as host:
18+
april_led = host.arms.get("april-led")
19+
assert april_led
20+
digital_outputs = april_led.digital_outputs
21+
assert digital_outputs
22+
digital_outputs_by_type = digital_outputs.get("april-led")
23+
assert digital_outputs_by_type
24+
output = digital_outputs_by_type.get("")
25+
assert output
26+
led_state = False
27+
while not host.is_closed():
28+
print("Set led state", led_state)
29+
output.set_pin_state("", led_state)
30+
while not host.is_closed():
31+
state = output.fetch_state()
32+
if not state:
33+
return
34+
pin_state = state.get_pin_state("")
35+
if pin_state and pin_state.state == led_state:
36+
break
37+
time.sleep(5.0)
38+
led_state = not led_state
39+
40+
41+
if __name__ == "__main__":
42+
flags.DEFINE_string(
43+
"connection_string", "", "Connect using a PyReach connection string (see "
44+
"connection_string.md for examples and documentation).")
45+
app.run(main)

pyreach/gyms/docs/gym_api.md

Lines changed: 40 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1235,35 +1235,57 @@ initialized with the with a dictionary of the following structure:
12351235

12361236
```
12371237
{
1238-
"digital_outputs": {
1239-
"gym_pin_name1":
1240-
("reach_name", "capability_type", "reach_pin_name"),
1241-
"gym_pin_name2":
1242-
("reach_name", "capability_type", "reach_pin_name"),
1243-
# ...
1244-
"gym_pin_nameN":
1245-
("reach_name", "capability_type", "reach_pin_name"),
1246-
},
1247-
# Eventually, there will be "digital_inputs", "analog_outputs"
1248-
# and "analog_inputs", but not for now.
1238+
"io":
1239+
io_element.ReachIO(
1240+
reach_name="reach_io_device_name",
1241+
is_synchronous=True/False, # synchronous/asynchronous IO device
1242+
digital_outputs={ # One dict entry for each digital output
1243+
"gym_digital_output1":
1244+
io_element.ReachDigitalOutuput(
1245+
reach_name="",
1246+
capability_type="",
1247+
pin_name="")
1248+
# ...
1249+
"gym_digital_outputN":
1250+
io_element.ReachDigitalOutuput(
1251+
reach_name="",
1252+
capability_type="",
1253+
pin_name="")
1254+
)}
1255+
# digital_inputs={...} # Not implemented yet
1256+
# analog_outputs={...} # Not implemented yet
1257+
# analog_inputs={...} # Not implemented yet
12491258
}
12501259
```
12511260

1252-
The `"digital_outputs"` configuration space consists of a dictionary of type
1253-
`Dict[str, Tuple(str, str, str)]` where the key is a user specified name (e.g.
1254-
`"gym_pin_nameI"`) with a three string tuple of the form `("reach_name"",
1255-
"capability_type", "reach_pin_name")`, where
1261+
when the action space is filled in, it will look as follows:
12561262

1257-
* `reach_name`: The reach device that hosts the I/O pin.
1263+
```
1264+
action["io"]["digital_outputs"] = {
1265+
"digital_output1": 0, # Turn off,
1266+
"digital_outputN": 1, # Turn on,
1267+
}
1268+
```
12581269

1259-
* `capability_type`: The capability type string for the pin. This is specified
1270+
The three values fed into the `io_element.ReachDigitalOutput` are:
1271+
1272+
* `reach_name`: The name of the controller that is managing the I/O. This is
1273+
frequently the same name as the Arm name, but not always.
1274+
1275+
* `capability_type`: The capability type string for the pin. This is specifie
12601276
by the robot configuration and is supplied by the operation team.
12611277

12621278
* `reach_pin_name`: The specific pin name for the capability. Again, this is
12631279
specified by the robot configuration and is supplied by the operation team.
12641280

1281+
In practice, trying to the correct values for these 3 strings can be quite
1282+
challenging. A useful trick/hack is to specify random strings for these three
1283+
strings. The I/O will complain about ones that are incorrect *AND* provide a
1284+
list of value values. Thus, tremendously simplifies the task of trying to figure
1285+
what values to used.
1286+
12651287
Eventually, as other I/O types show up, additional top level configuration
1266-
dictionaries will be added (e.g. `digital_inputs`, etc.)
1288+
dictionary enteries are anticipated (e.g. `digital_inputs`, etc.)
12671289

12681290
#### Oracle Configuration
12691291

pyreach/gyms/reach_env.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ def __init__(self,
143143
host: Optional[pyreach.Host] = None,
144144
gym_env_id: Optional[str] = None,
145145
connection_string: Optional[str] = None,
146+
robot_types: Optional[Dict[str, str]] = None,
146147
**kwargs: Any) -> None:
147148
"""Initialize a Reach Gym Environment.
148149
@@ -153,6 +154,7 @@ def __init__(self,
153154
host: A host to use. (Default: None.)
154155
gym_env_id: ID used to create this gym. Must be specified.
155156
connection_string: the connection string (see connection_string.md).
157+
robot_types: Overrides for robot types.
156158
**kwargs: Additional keyword arguments.
157159
158160
Raises:
@@ -242,6 +244,8 @@ def __init__(self,
242244
"enable_streaming": False,
243245
"arm_default_ik_types": arm_default_ik_types,
244246
}
247+
if robot_types is not None:
248+
host_kwargs["robot_types"] = robot_types.copy()
245249

246250
if not host:
247251
if not connection_string:

pyreach/impl/arm_impl.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,8 @@ def from_urdf_file(cls, urdf_file: str) -> arm.ArmType:
210210
"FanucR2000ia165f.urdf", "XArm6.urdf"
211211
}:
212212
return ArmTypeImpl(urdf_file, 6)
213+
if urdf_file == "april-led.urdf":
214+
return ArmTypeImpl(urdf_file, 0)
213215
raise ValueError("invalid URDF file: " + urdf_file)
214216

215217

@@ -1232,7 +1234,9 @@ def support_blowoff(self) -> bool:
12321234

12331235
def set_ik_lib(self, ik_lib: arm.IKLibType) -> None:
12341236
"""Set the IK library to be used."""
1235-
if ik_lib == arm.IKLibType.IKFAST:
1237+
if self._arm_type.joint_count == 0:
1238+
self._ik_lib = None
1239+
elif ik_lib == arm.IKLibType.IKFAST:
12361240
self._ik_lib = IKLibIKFast(self._arm_type.urdf_file)
12371241
elif ik_lib == arm.IKLibType.IKPYBULLET:
12381242
if self._arm_type.urdf_file != "XArm6.urdf":

pyreach/impl/host_impl.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,16 +153,18 @@ def __init__(
153153
enable_streaming: bool = True,
154154
take_control_at_start: bool = True,
155155
arm_default_ik_types: Optional[Dict[str, arm.IKLibType]] = None,
156+
robot_types: Optional[Dict[str, str]] = None,
156157
) -> None:
157158
"""Initialize the Host.
158159
159160
Args:
160161
client: Connection to the client.
161-
arm_types: Overrides for arm types.
162+
arm_types: Overrides for robot types, deviceName to URDF file mapping.
162163
enable_streaming: If True, automatically start streaming for Arm,
163164
ColorCamera and DepthCamera devices.
164165
take_control_at_start: If True, immediately take control.
165166
arm_default_ik_types: Default ik type for arms.
167+
robot_types: Overrides for robot types, deviceName to URDF file mapping.
166168
167169
Raises:
168170
Exception: when failed to load config.
@@ -247,6 +249,12 @@ def __init__(
247249
interfaces = self._config.machine_interfaces
248250

249251
def get_arm_type(name: str) -> Optional[arm.ArmType]:
252+
if robot_types and name in robot_types:
253+
try:
254+
return arm_impl.ArmTypeImpl.from_urdf_file(robot_types[name])
255+
except ValueError:
256+
logging.warning("Robot type override %s for %s invalid",
257+
robot_types[name], name)
250258
if arm_types and name in arm_types:
251259
try:
252260
return arm_impl.ArmTypeImpl.from_urdf_file(arm_types[name])

0 commit comments

Comments
 (0)