Skip to content

Commit ed1ac8a

Browse files
authored
Merge pull request #6 from OSUrobotics/L-fp
L fp
2 parents e066a87 + b4b9e4e commit ed1ac8a

File tree

9 files changed

+267
-139
lines changed

9 files changed

+267
-139
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,4 +170,4 @@ cython_debug/
170170
*.pkl
171171
pkl/
172172

173-
meshes/
173+
meshes*/

README.md

Lines changed: 41 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,61 @@
11

22
## Using this package
33

4-
### Adding trees
5-
All trees should be defined by their origin namespace, the tree type, and the tree id. Tree ids should be zero-padded by 5 spaces.
4+
### Installation
65

7-
```
8-
# Pattern:
9-
{tree_namespace}_{tree_type}_{tree_id}
6+
#### Docker
7+
If using Ubuntu, you may skip this step. All other distributions must create a Docker environment. This container includes ROS2 Humble.
108

11-
# Examples:
12-
LPy_envy_00027
13-
prosser_ufo_00762
9+
```
10+
docker run -it --net=host --device /dev/dri/ -e DISPLAY=$DISPLAY -v $HOME/.Xauthority:/root/.Xauthority:ro osrf/ros:humble-desktop
1411
```
1512

16-
Trees should include a generic mesh and and a labeled mesh. LPy trees can be generated `TODO: TALK TO ABHINAV`
17-
18-
19-
## TODO:
20-
Look up Cantera
21-
22-
urdf generic launch CLI test:
23-
`xacro robot.urdf.xacro > test.urdf end_effector_type:=mock_pruner eef_parent:=ur5e__tool0 arm_type:=ur5 ur_type:=ur5e tf_prefix:=ur5e__ base_attachment_type:=linear_slider`
24-
25-
## TODO
26-
1. Figure out better sensor -> camera -> ToF inheritance (ask Cindy)
27-
1. Tree/robot/env interaction
28-
1. For Claire:
29-
1. Figure out best way to manage tree/robot/environment interaction. I removed robot from penv, but self.trees still exists.
30-
1. Fill out the `object_loader.py` class. Activate/deactivate trees, supports, robots.
31-
1. Find the `TODO`s in all the code. Ask Luke what they meana and discuss solutions.
32-
1. Format the final approach controller as a python subpackage?
33-
1. https://packaging.python.org/en/latest/guides/packaging-namespace-packages/#packaging-namespace-packages
34-
1. Add basic cylinder to world. Dynamically create URDF.
35-
1. Dynamically populate UR URDF. Allow for various end-effectors and robot configurations.
36-
1. Make sure to include camera and other sensors. (Source manifold mesh -- utils -> camera class (C++))
37-
1. Dynamic parent joint for Panda to slider/farm-ng (like UR5)
38-
1. Add generic robot class (from Abhinav's code)
39-
1. Panda/UR5
40-
1. End-effector
41-
1. Make mini orchard from a set of URDFs
42-
1. Space out like normal orchard
43-
1. Be aware of memory issues in PyB
44-
1. Change pkl files to hdf5. (lower priority) -- Ask Abhinav for code
45-
1. Test
46-
1. Various tof configurations
13+
After the docker environment has downloaded, update the system and download the UR robot drivers:
14+
```
15+
apt update -y && apt upgrade -y
16+
apt install python3-venv -y
17+
apt install ros-$ROS_DISTRO-ur -y
18+
cd ~
19+
```
4720

48-
## Installation
4921

50-
#### Requirements
51-
ur, linear_slider, franka-emika
22+
#### Installing this package
23+
1. Clone this repository into your local directory:
24+
```
25+
git clone https://github.com/OSUrobotics/pybullet-tree-sim.git
26+
```
27+
2. Create a virtual environment. Python's `venv` is encouraged:
28+
```
29+
cd pybullet-tree-sim
30+
python3 -m venv venv
31+
source venv/bin/activate
32+
```
5233

53-
#### General use
34+
3. Install using pip:
5435
```
55-
python -m pip install .
36+
python3 -m pip install --upgrade pip
37+
pip install .
5638
```
5739

58-
#### Development
40+
4. Download the required mesh files from Zenodo
41+
If successfully installed, a CLI arg `mesh_downloader` should now be active in your environment.
5942
```
60-
python -m pip install -e.
43+
mesh_downloader
6144
```
45+
This command will extract the zip file for you. It is a large file and will take several minutes.
6246

6347

64-
### Useful RegEx commands...
65-
For replacing .mtl file paths
48+
### Adding trees
49+
All trees should be defined by their origin namespace, the tree type, and the tree id. Tree ids should be zero-padded by 5 spaces.
50+
6651
```
67-
From: ([a-zA-Z0-9_]*).mtl\n
68-
To: ../mtl/$1.mtl\n
52+
# Pattern:
53+
{tree_namespace}_{tree_type}_{tree_id}
6954
55+
# Examples:
56+
LPy_envy_00027
57+
prosser_ufo_00762
7058
```
59+
60+
Trees should include a generic mesh and and a labeled mesh. LPy trees can be generated by `TODO: TALK TO ABHINAV` and added to the Zenodo storage.
61+

TODO.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# TODO:
2+
Look up Cantera
3+
4+
urdf generic launch CLI test:
5+
`xacro robot.urdf.xacro > test.urdf end_effector_type:=mock_pruner eef_parent:=ur5e__tool0 arm_type:=ur5 ur_type:=ur5e tf_prefix:=ur5e__ base_attachment_type:=linear_slider`
6+
7+
## TODO
8+
1. Figure out better sensor -> camera -> ToF inheritance (ask Cindy)
9+
1. Tree/robot/env interaction
10+
1. For Claire:
11+
1. Figure out best way to manage tree/robot/environment interaction. I removed robot from penv, but self.trees still exists.
12+
1. Fill out the `object_loader.py` class. Activate/deactivate trees, supports, robots.
13+
1. Find the `TODO`s in all the code. Ask Luke what they meana and discuss solutions.
14+
1. Format the final approach controller as a python subpackage?
15+
1. https://packaging.python.org/en/latest/guides/packaging-namespace-packages/#packaging-namespace-packages
16+
1. Add basic cylinder to world. Dynamically create URDF.
17+
1. Dynamically populate UR URDF. Allow for various end-effectors and robot configurations.
18+
1. Make sure to include camera and other sensors. (Source manifold mesh -- utils -> camera class (C++))
19+
1. Dynamic parent joint for Panda to slider/farm-ng (like UR5)
20+
1. Add generic robot class (from Abhinav's code)
21+
1. Panda/UR5
22+
1. End-effector
23+
1. Make mini orchard from a set of URDFs
24+
1. Space out like normal orchard
25+
1. Be aware of memory issues in PyB
26+
1. Change pkl files to hdf5. (lower priority) -- Ask Abhinav for code
27+
1. Test
28+
1. Various tof configurations
29+
30+
31+
### Useful RegEx commands...
32+
For replacing .mtl file paths
33+
```
34+
From: ([a-zA-Z0-9_]*).mtl\n
35+
To: ../mtl/$1.mtl\n
36+
37+
```

main.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
def main():
1717
pbutils = PyBUtils(renders=True)
18-
robot_start_orientation = Rotation.from_euler('xyz', [0, 0,180], degrees=True).as_quat()
18+
robot_start_orientation = Rotation.from_euler("xyz", [0, 0, 180], degrees=True).as_quat()
1919
robot = Robot(pbclient=pbutils.pbclient, position=[0, 1, 0], orientation=robot_start_orientation)
2020

2121
penv = PruningEnv(
@@ -42,7 +42,7 @@ def main():
4242
save_tree_urdf=False,
4343
# randomize_pose=True
4444
)
45-
penv.activate_tree(tree_id_str=tree_name)
45+
penv.activate_tree_by_id_str(tree_id_str=tree_name)
4646

4747
# # Run the sim a little just to get the environment properly loaded.
4848
for i in range(100):

pybullet_tree_sim/pruning_environment.py

Lines changed: 31 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def __init__(
6969
# make_trees: bool = False,
7070
name: str = "PruningEnv",
7171
# num_trees: int | None = None,
72-
renders: bool = False,
72+
# renders: bool = False,
7373
verbose: bool = True,
7474
) -> None:
7575
"""Initialize the Pruning Environment
@@ -83,7 +83,7 @@ def __init__(
8383

8484
# Pybullet GUI variables
8585
self.render_mode = "rgb_array"
86-
self.renders = renders
86+
# self.renders = renders
8787
# self.eval = evaluate
8888

8989
# Gym variables
@@ -143,61 +143,54 @@ def load_tree( # TODO: Clean up Tree init vs create_tree, probably not needed.
143143
randomize_pose=randomize_pose,
144144
)
145145

146-
tree_id_str = f"{tree_namespace}{tree_type}_tree{tree_id}"
147-
# urdf_path = os.path.join(URDF_PATH, "trees", tree_type, "generated", f"{tree.id_str}.urdf")
148-
149146
# Add tree to dict of trees
150147
self.trees[tree.id_str] = tree
151-
return tree_id_str
148+
return tree.id_str
149+
150+
def get_tree_from_id_str(self, tree_id_str: str) -> Tree:
151+
try:
152+
tree = self.trees[tree_id_str]
153+
except KeyError as e:
154+
raise TreeException(f"{e}: Tree with ID {tree_id_str} not found")
155+
return tree
152156

153157
def activate_tree(
154158
self,
155-
tree: Tree | None = None,
156-
tree_id_str: str | None = None,
159+
tree: Tree,
157160
include_support_posts: bool = True,
158161
) -> None:
159162
"""Activate a tree by object or by tree_id_str. Can include support posts. Must provide either a Tree or tree_id_str.
160163
@param tree (Tree/None): Tree object to be activated into the pruning environment.
161164
@param tree_id_str (str/None): String including the identification characteristics of the tree.
162165
@return None
163166
"""
164-
165-
if tree is None and tree_id_str is None:
166-
raise TreeException("Parameters 'tree' and 'tree_id_str' cannot both be None")
167-
168-
if tree is None and tree_id_str is not None:
169-
try:
170-
tree = self.trees[tree_id_str]
171-
except KeyError as e:
172-
raise TreeException(f"{e}: Tree with ID {tree_id_str} not found")
173-
174-
if tree is not None:
167+
if tree:
175168
if self.verbose:
176169
log.info("Activating tree")
177-
tree.pyb_tree_id = self.pbutils.pbclient.loadURDF(tree.urdf_path, useFixedBase=True)
178-
log.info(f"Tree {tree.id_str} activated with PyBID {tree.pyb_tree_id}")
170+
tree.pyb_id = self.pbutils.pbclient.loadURDF(tree.urdf_path, useFixedBase=True)
171+
log.info(f"Tree {tree.id_str} activated with PyBID {tree.pyb_id}")
179172

180173
if include_support_posts:
181174
self.activate_support_posts(associated_tree=tree)
182175
return
183176

184-
def deactivate_tree(self, tree: Tree | None = None, tree_id_str: str | None = None) -> None:
185-
"""Deactivate a tree by object or by tree_id_str"""
186-
if tree is None and tree_id_str is None:
187-
raise TreeException("Parameters 'tree' and 'tree_id_str' cannot both be None")
188-
189-
if tree is None and tree_id_str is not None:
190-
try:
191-
tree = self.trees[tree_id_str]
192-
except KeyError as e:
193-
raise TreeException(f"{e}: Tree with ID {tree_id_str} not found")
194-
195-
if tree is not None:
196-
try:
197-
self.pbutils.pbclient.removeBody(tree.pyb_tree_id)
198-
log.info(f"Tree {tree.id_str} with PyBID {tree.pyb_tree_id} deactivated")
199-
except Exception as e:
200-
log.error(f"Error deactivating tree: {e}")
177+
def activate_tree_by_id_str(self, tree_id_str: str, include_support_posts: bool = True) -> None:
178+
tree = self.get_tree_from_id_str(tree_id_str=tree_id_str)
179+
self.activate_tree(tree=tree, include_support_posts=include_support_posts)
180+
return
181+
182+
def deactivate_tree(self, tree: Tree) -> None:
183+
"""Deactivate a tree object"""
184+
try:
185+
self.pbutils.pbclient.removeBody(tree.pyb_id)
186+
log.info(f"Tree {tree.id_str} with PyBID {tree.pyb_id} deactivated")
187+
except Exception as e:
188+
log.error(f"Error deactivating tree: {e}")
189+
return
190+
191+
def deactivate_tree_by_id_str(self, tree_id_str: str) -> None:
192+
tree = self.get_tree_from_id_str(tree_id_str=tree_id_str)
193+
self.deactivate_tree(tree=tree)
201194
return
202195

203196
def activate_support_posts(

pybullet_tree_sim/robot.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def __init__(
4444
self.position = position
4545
self.orientation = orientation
4646
self.randomize_pose = randomize_pose # TODO: This isn't set up anymore... fix
47-
self.init_joint_angles = (
47+
self.init_joint_angles = ( # TODO: dynamically assign from defaults (i.e. if linear-slider is loaded in)
4848
(
4949
-np.pi / 2 + np.pi / 4,
5050
-np.pi * 2 / 3,
@@ -172,6 +172,7 @@ def _setup_robot(self):
172172
def _get_joints(self) -> dict:
173173
"""Return a dict of joint information for the robot"""
174174
joints = {}
175+
175176
for i in range(self.num_joints):
176177
info = self.pbclient.getJointInfo(self.robot, i)
177178
joint_name = info[1].decode("utf-8")
@@ -187,16 +188,25 @@ def _get_joints(self) -> dict:
187188
}
188189
}
189190
)
191+
log.warn(joints)
190192
return joints
191193

192-
def _assign_control_joints(self, joints: dict) -> list:
194+
def _assign_control_joints(self, joints: dict) -> tuple[list]:
193195
"""Get list of controllable joints from the joint dict by joint type"""
194196
control_joints = []
195197
control_joint_idxs = []
198+
self.control_joint_lower_limits = []
199+
self.control_joint_upper_limits = []
200+
self.control_joint_ranges = []
196201
for joint, joint_info in joints.items():
197202
if joint_info["type"] == 0: # TODO: Check if this works for prismatic joints or just revolute
198203
control_joints.append(joint)
199204
control_joint_idxs.append(joint_info["id"])
205+
206+
# self.joint_upper_limits,
207+
# self.joint_lower_limits,
208+
# self.joint_ranges, # ,
209+
200210
return control_joints, control_joint_idxs
201211

202212
def _get_links(self) -> dict:
@@ -323,6 +333,7 @@ def reset_robot(self, joint_angles: tuple = None) -> None:
323333
else joint_angles
324334
)
325335
self.set_joint_angles_no_collision(self.init_joint_angles)
336+
# self.pbclient.resetJointState() # TODO Fill out
326337
return
327338

328339
def remove_robot(self):
@@ -565,7 +576,7 @@ def check_collisions(self, collision_objects) -> Tuple[bool, dict]:
565576
collisons_self = self.pbclient.getContactPoints(bodyA=self.robot, bodyB=self.robot)
566577
collisions_unacceptable = collisons_self
567578
for i in range(len(collisions_unacceptable)):
568-
if collisions_unacceptable[i][-6] < -0.001:
579+
if collisions_unacceptable[i][-6] < -0.00:
569580
collision_info["collisions_unacceptable"] = True
570581
break
571582
if self.verbose > 1:
@@ -578,7 +589,7 @@ def check_collisions(self, collision_objects) -> Tuple[bool, dict]:
578589

579590
def check_success_collision(self, body_b) -> bool:
580591
"""Check if there are any collisions between the robot and the environment
581-
Returns: Boolw
592+
Returns: Bool
582593
"""
583594
collisions_success = self.pbclient.getContactPoints(
584595
bodyA=self.robot, bodyB=body_b, linkIndexA=self.success_link_index

0 commit comments

Comments
 (0)