Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

initial action_library integration #16

Merged
merged 7 commits into from
Sep 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/social_norms_trees/behavior_library.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class BehaviorLibrary:
def __init__(self, behaviors):
self.behaviors = behaviors

def get_behavior_by_nickname(self, nickname):
return self.behaviors.get(nickname)

def get_behavior_by_id(self, id_):
for behavior in self.behaviors.values():
if behavior.id == id_:
return behavior
return None
15 changes: 15 additions & 0 deletions src/social_norms_trees/custom_node_library.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import py_trees

class CustomBehavior(py_trees.behaviours.Dummy):
def __init__(self, name, id_, nickname):
super().__init__(name)
self.id_ = id_
self.nickname = nickname



# id of the behavior within the behavior library (persists)
# but also the unique id for the behavior within the tree (in case there are multiple instances of
# the behavior in one tree)


134 changes: 129 additions & 5 deletions src/social_norms_trees/mutate_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
import click
import py_trees

from datetime import datetime

from social_norms_trees.custom_node_library import CustomBehavior


T = TypeVar("T", bound=py_trees.behaviour.Behaviour)

Expand Down Expand Up @@ -337,19 +341,32 @@ def remove_node(tree: T, node: Optional[py_trees.behaviour.Behaviour] = None) ->
warnings.warn(
f"{node}'s parent is None, so we can't remove it. You can't remove the root node."
)
return tree
action_log = {}
return tree,
elif isinstance(parent_node, py_trees.composites.Composite):
parent_node.remove_child(node)
action_log = {
"type": "remove_node",
"nodes": [
{
"id_": node.id_,
"nickname": node.nickname
},
],
"timestamp": datetime.now().isoformat(),
}
else:
raise NotImplementedError()
return tree

return tree, action_log


def move_node(
tree: T,
node: Optional[py_trees.behaviour.Behaviour] = None,
new_parent: Optional[py_trees.behaviour.Behaviour] = None,
index: int = None,
internal_call: bool = False,
) -> T:
"""Exchange two behaviours in the tree

Expand All @@ -370,9 +387,24 @@ def move_node(
assert isinstance(new_parent, py_trees.composites.Composite)
assert isinstance(node.parent, py_trees.composites.Composite)

# old_parent = node.parent.name
node.parent.remove_child(node)
new_parent.insert_child(node, index)


if not internal_call:
action_log = {
"type": "move_node",
"nodes": [
{
"id": node.id_,
"nickname": node.nickname,
},
],
"timestamp": datetime.now().isoformat(),
}
return tree, action_log

return tree


Expand Down Expand Up @@ -435,7 +467,99 @@ def exchange_nodes(
node0_parent, node0_index = node0.parent, node0.parent.children.index(node0)
node1_parent, node1_index = node1.parent, node1.parent.children.index(node1)

tree = move_node(tree, node0, node1_parent, node1_index)
tree = move_node(tree, node1, node0_parent, node0_index)
tree = move_node(tree, node0, node1_parent, node1_index, True)
tree = move_node(tree, node1, node0_parent, node0_index, True)

nodes = []
if node0.__class__.__name__ != "CustomBehavior":
nodes.append(
{
"nickname": node0.name,
}
)
else:
nodes.append(
{
"id": node0.id_,
"nickname": node0.nickname
}
)

if node1.__class__.__name__ != "CustomBehavior":
nodes.append(
{
"nickname": node1.name,
}
)
else:
nodes.append(
{
"id": node1.id_,
"nickname": node1.nickname
}
)

action_log = {
"type": "exchange_nodes",
"nodes": nodes,
"timestamp": datetime.now().isoformat(),
}
return tree, action_log


def prompt_select_node(behavior_library, text):

for idx, tree_name in enumerate(behavior_library.behaviors.keys(), 1):
print(f"{idx}. {tree_name}")


node_index = click.prompt(
text=text,
type=int,
)

node_key = list(behavior_library.behaviors.keys())[node_index-1]

return behavior_library.behaviors[node_key]


def add_node(
tree: T,
behavior_library: object,
) -> T:
"""Exchange two behaviours in the tree

Examples:
>>> tree = py_trees.composites.Sequence("", False, children=[])

"""


behavior = prompt_select_node(behavior_library, f"Which behavior do you want to add?")

new_node = CustomBehavior(
name=behavior['nickname'],
id_=behavior['id'],
nickname=behavior['nickname']
)

new_parent = prompt_identify_parent_node(
tree, f"What should its parent be?", display_nodes=True
)

index = prompt_identify_child_index(new_parent)

assert isinstance(new_parent, py_trees.composites.Composite)

return tree
new_parent.insert_child(new_node, index)

action_log = {
"type": "add_node",
"node": {
"id": new_node.id_,
"nickname": new_node.nickname
},
"timestamp": datetime.now().isoformat(),
}

return tree, action_log
104 changes: 104 additions & 0 deletions src/social_norms_trees/resources.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
{
"behavior_tree": {
"name": "Sequence A",
"type": "Sequence",
"children": [
{
"name": "Behavior A",
"type": "Behavior",
"children": []
},
{
"name": "Behavior B",
"type": "Behavior",
"children": []
},
{
"name": "Sequence B",
"type": "Sequence",
"children": [
{
"name": "Behavior C",
"type": "Behavior",
"children": []
},
{
"name": "Behavior D",
"type": "Behavior",
"children": []
}
]
},
{
"name": "Sequence C",
"type": "Sequence",
"children": [
{
"name": "Behavior E",
"type": "Behavior",
"children": []
},
{
"name": "Behavior F",
"type": "Behavior",
"children": []
},
{
"name": "Behavior G",
"type": "Behavior",
"children": []
},
{
"name": "Behavior H",
"type": "Behavior",
"children": []
}
]
}
]
},
"behavior_library":
{
"Behavior A": {
"id": "001",
"nickname": "Behavior A",
"type": "Dummy"
},
"Behavior B": {
"id": "002",
"nickname": "Behavior B",
"type": "Dummy"
},
"Behavior C": {
"id": "003",
"nickname": "Behavior C",
"type": "Dummy"
},
"Behavior D": {
"id": "004",
"nickname": "Behavior D",
"type": "Dummy"
},
"Behavior E": {
"id": "005",
"nickname": "Behavior E",
"type": "Dummy"
},
"Behavior F": {
"id": "006",
"nickname": "Behavior F",
"type": "Dummy"
},
"Behavior G": {
"id": "007",
"nickname": "Behavior G",
"type": "Dummy"
},
"Behavior H": {
"id": "008",
"nickname": "Behavior H",
"type": "Dummy"
}

}
}
23 changes: 13 additions & 10 deletions src/social_norms_trees/serialize_tree.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import py_trees
from social_norms_trees.custom_node_library import CustomBehavior


def serialize_tree(tree):
Expand All @@ -12,20 +13,22 @@ def serialize_node(node):

return serialize_node(tree)

def deserialize_tree(tree):
def deserialize_tree(tree, behavior_library):
def deserialize_node(node):
node_type = node['type']
children = [deserialize_node(child) for child in node['children']]

if node_type == 'Sequence':
return py_trees.composites.Sequence(node['name'], False, children=children)
elif node_type == 'Dummy':
return py_trees.behaviours.Dummy(node['name'])
elif node_type == 'Success':
return py_trees.behaviours.Success(node['name'])
elif node_type == 'Failure':
return py_trees.behaviours.Failure(node['name'])
elif node_type == 'Running':
return py_trees.behaviours.Running(node['name'])

elif node_type == 'Behavior':
behavior = behavior_library.get_behavior_by_nickname(node['name'])
if behavior:
return CustomBehavior(
name=behavior['nickname'],
id_=behavior['id'],
nickname=behavior['nickname']
)
else:
raise ValueError(f"Behavior {node['name']} not found in behavior library")

return deserialize_node(tree)
Loading