Skip to content

Commit

Permalink
Minor fixes to URDF components.
Browse files Browse the repository at this point in the history
  • Loading branch information
senthurayyappan committed Dec 13, 2024
1 parent 6558091 commit 11093cd
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 20 deletions.
6 changes: 3 additions & 3 deletions onshape_api/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ async def traverse_instances_async(


def get_instances(
assembly: Assembly, max_depth: int = 5
assembly: Assembly, max_depth: int = 0
) -> tuple[dict[str, Union[PartInstance, AssemblyInstance]], dict[str, Occurrence], dict[str, str]]:
"""
Optimized synchronous wrapper for `get_instances`.
Expand All @@ -109,7 +109,7 @@ def get_instances(


def get_instances_sync(
assembly: Assembly, max_depth: int = 5
assembly: Assembly, max_depth: int = 0
) -> tuple[dict[str, Union[PartInstance, AssemblyInstance]], dict[str, Occurrence], dict[str, str]]:
"""
Get instances and their sanitized names from an Onshape assembly.
Expand Down Expand Up @@ -188,7 +188,7 @@ def traverse_instances(
return instance_map, occurrence_map, id_to_name_map


def get_occurrences(assembly: Assembly, id_to_name_map: dict[str, str], max_depth: int = 5) -> dict[str, Occurrence]:
def get_occurrences(assembly: Assembly, id_to_name_map: dict[str, str], max_depth: int = 0) -> dict[str, Occurrence]:
"""
Optimized occurrences fetching using comprehensions.
"""
Expand Down
2 changes: 1 addition & 1 deletion onshape_api/robot.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ def get_robot(
robot_name: str,
url: str,
env: str = "./.env",
max_traversal_depth: int = 5,
max_traversal_depth: int = 0,
use_user_defined_root: bool = False,
save_assembly_as_json: bool = False,
robot_type: RobotType = RobotType.URDF,
Expand Down
36 changes: 23 additions & 13 deletions onshape_api/urdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
VisualLink,
)
from onshape_api.parse import CHILD, MATE_JOINER, PARENT, RELATION_PARENT
from onshape_api.utilities.helpers import get_sanitized_name, make_unique_keys
from onshape_api.utilities.helpers import get_sanitized_name, make_unique_keys, make_unique_name

SCRIPT_DIR = os.path.dirname(__file__)

Expand Down Expand Up @@ -254,10 +254,10 @@ def get_robot_joint(

elif mate.mateType == MateType.BALL:
dummy_x = Link(
name=f"{parent}-{mate.name}-x",
name=f"{parent}-{get_sanitized_name(mate.name)}-x",
)
dummy_y = Link(
name=f"{parent}-{mate.name}-y",
name=f"{parent}-{get_sanitized_name(mate.name)}-y",
)

links = [dummy_x, dummy_y]
Expand Down Expand Up @@ -413,6 +413,8 @@ def get_urdf_components(
joints_map = {}
assets_map = {}

parts_traversed_map: dict[str, str] = {}

topological_mates, topological_relations = get_topological_mates(graph, mates, relations)

stl_to_link_tf_map = {}
Expand All @@ -424,7 +426,7 @@ def get_urdf_components(
)

links.append(root_link)

parts_traversed_map[root_node] = parts[root_node].uid
assets_map[root_node] = _asset
stl_to_link_tf_map[root_node] = stl_to_root_tf

Expand All @@ -435,17 +437,11 @@ def get_urdf_components(
parent, child = edge
mate_key = f"{parent}{MATE_JOINER}{child}"
LOGGER.info(f"Processing edge: {parent} -> {child}")

try:
parent_tf = stl_to_link_tf_map[parent]
except KeyError:
LOGGER.warning(f"Parent {parent} not found in stl_to_link_tf_map")
LOGGER.info(f"stl_to_link_tf_map keys: {stl_to_link_tf_map.keys()}")
exit(1)
parent_tf = stl_to_link_tf_map[parent]

if parent not in parts or child not in parts:
LOGGER.warning(f"Part {parent} or {child} not found in parts")
# remove the edge from the graph
# remove the edge from the graph?
continue

relation = topological_relations.get(topological_mates[mate_key].id)
Expand Down Expand Up @@ -484,9 +480,23 @@ def get_urdf_components(
client,
topological_mates[mate_key],
)

stl_to_link_tf_map[child] = stl_to_link_tf
assets_map[child] = asset
links.append(link)

if child not in parts_traversed_map:
links.append(link)
parts_traversed_map[child] = parts[child].uid
elif child in parts_traversed_map and parts_traversed_map[child] != parts[child].uid:
LOGGER.warning(f"Part shares the same name but different UID: {child}")
LOGGER.info("Still adding the link with modified unique name.")
unique_name = make_unique_name(link.name, set(parts_traversed_map))
parts_traversed_map[unique_name] = parts[child].uid
link.name = unique_name
links.append(link)
else:
LOGGER.warning(f"Part {child} already traversed. Skipping.")
continue

unique_joint_key_map = make_unique_keys([joint.name for joint in joints])
unique_link_key_map = make_unique_keys([link.name for link in links])
Expand Down
42 changes: 39 additions & 3 deletions onshape_api/utilities/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import json
import os
import random
import re
from xml.sax.saxutils import escape

import matplotlib.animation as animation
Expand Down Expand Up @@ -238,10 +239,38 @@ def make_unique_keys(keys: list[str]) -> dict[str, int]:
return unique_key_map


def make_unique_name(name: str, existing_names: set[str]) -> str:
"""
Make a name unique by appending a number to the name if it already exists in a set.
Args:
name: Name to make unique.
existing_names: Set of existing names.
Returns:
A unique name.
Examples:
>>> make_unique_name("name", {"name"})
"name-1"
>>> make_unique_name("name", {"name", "name-1"})
"name-2"
"""
if name not in existing_names:
return name

count = 1
while f"{name}-{count}" in existing_names:
count += 1

return f"{name}-{count}"


def get_sanitized_name(name: str, replace_with: str = "-") -> str:
"""
Sanitize a name by removing special characters, preserving "-" and "_", and
replacing spaces with a specified character.
replacing spaces with a specified character. Ensures no consecutive replacement
characters in the result.
Args:
name: Name to sanitize
Expand All @@ -253,15 +282,22 @@ def get_sanitized_name(name: str, replace_with: str = "-") -> str:
Examples:
>>> get_sanitized_name("wheel1 <3>", '-')
"wheel1-3"
>>> get_sanitized_name("Hello World!", '_')
>>> get_sanitized_name("Hello World!", '_')
"Hello_World"
>>> get_sanitized_name("my--robot!!", '-')
"my-robot"
>>> get_sanitized_name("bad__name__", '_')
"bad_name"
"""

if replace_with not in "-_":
raise ValueError("replace_with must be either '-' or '_'")

sanitized_name = "".join(char if char.isalnum() or char in "-_ " else "" for char in name)
return sanitized_name.replace(" ", replace_with)
sanitized_name = sanitized_name.replace(" ", replace_with)
sanitized_name = re.sub(f"{re.escape(replace_with)}{{2,}}", replace_with, sanitized_name)

return sanitized_name


def show_video(frames, framerate=60):
Expand Down

0 comments on commit 11093cd

Please sign in to comment.