Skip to content

Conversation

@iory
Copy link
Owner

@iory iory commented Jan 5, 2026

  • Add urdf and include_mimic_joints parameters to RobotModel.init
  • Add _load_from_urdf method to auto-detect file path vs string
  • Simplify from_urdf to use RobotModel(urdf=...) internally
  • Update all robot classes (Panda, Fetch, Kuka, PR2, Nextage, R8_6) to inherit directly from RobotModel
  • Keep RobotModelFromURDF as deprecated alias for backward compatibility

- Add urdf and include_mimic_joints parameters to RobotModel.__init__
- Add _load_from_urdf method to auto-detect file path vs string
- Simplify from_urdf to use RobotModel(urdf=...) internally
- Update all robot classes (Panda, Fetch, Kuka, PR2, Nextage, R8_6)
  to inherit directly from RobotModel
- Keep RobotModelFromURDF as deprecated alias for backward compatibility
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors robot model classes to use a unified URDF loading approach through the RobotModel base class, simplifying the inheritance hierarchy and improving consistency across all robot models.

Key Changes:

  • Added urdf and include_mimic_joints parameters to RobotModel.__init__ for direct URDF loading during instantiation
  • Introduced _load_from_urdf() helper method for auto-detection of file paths vs URDF strings
  • Updated all robot classes (Panda, Fetch, Kuka, PR2, Nextage, R8_6) to inherit directly from RobotModel instead of RobotModelFromURDF

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
skrobot/model/robot_model.py Added urdf parameter to RobotModel constructor, implemented _load_from_urdf method, and simplified from_urdf to delegate to constructor
skrobot/models/urdf.py Added deprecation warning and refactored RobotModelFromURDF to use RobotModel constructor internally
skrobot/models/panda.py Changed inheritance to RobotModel, updated constructor with urdf/urdf_file parameters, removed default_urdf_path property
skrobot/models/fetch.py Changed inheritance to RobotModel, updated constructor with urdf/urdf_file parameters, removed default_urdf_path property
skrobot/models/kuka.py Changed inheritance to RobotModel, updated constructor with urdf/urdf_file parameters, removed default_urdf_path property
skrobot/models/pr2.py Changed inheritance to RobotModel, updated constructor with urdf/urdf_file parameters and use_tight_joint_limit support
skrobot/models/nextage.py Changed inheritance to RobotModel, updated constructor with urdf/urdf_file parameters, removed default_urdf_path property
skrobot/models/r8_6.py Changed inheritance to RobotModel, updated constructor with urdf/urdf_file parameters, removed legacy file existence check

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 15 to 22
def __init__(self, urdf=None, urdf_file=None, use_tight_joint_limit=True):
# For backward compatibility, support both urdf and urdf_file
if urdf is not None and urdf_file is not None:
raise ValueError(
"'urdf' and 'urdf_file' cannot be given at the same time"
)
urdf_input = urdf or urdf_file or pr2_urdfpath()
super(PR2, self).__init__(urdf=urdf_input)
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR2 class still retains the default_urdf_path property (defined later in the file), while other robot models (Panda, Fetch, Kuka, Nextage) had this property removed in the refactoring. For consistency, the default_urdf_path property should be removed from PR2 as well, since the URDF path is now handled through the constructor parameters.

Copilot uses AI. Check for mistakes.
Comment on lines 8 to 15
def __init__(self, urdf=None, urdf_file=None):
# For backward compatibility, support both urdf and urdf_file
# Also support legacy urdf_path argument via urdf_file
if urdf is not None and urdf_file is not None:
raise ValueError(
"'urdf' and 'urdf_file' cannot be given at the same time"
)
urdf_input = urdf or urdf_file or r8_6_urdfpath()
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment mentions "Also support legacy urdf_path argument via urdf_file", but the function signature doesn't include a urdf_path parameter. If the intention is to support urdf_path for backward compatibility, it should be added to the signature (e.g., def __init__(self, urdf=None, urdf_file=None, urdf_path=None)) and handled appropriately, or the comment should be removed to avoid confusion.

Suggested change
def __init__(self, urdf=None, urdf_file=None):
# For backward compatibility, support both urdf and urdf_file
# Also support legacy urdf_path argument via urdf_file
if urdf is not None and urdf_file is not None:
raise ValueError(
"'urdf' and 'urdf_file' cannot be given at the same time"
)
urdf_input = urdf or urdf_file or r8_6_urdfpath()
def __init__(self, urdf=None, urdf_file=None, urdf_path=None):
# For backward compatibility, support both urdf and urdf_file
# Also support legacy urdf_path argument via urdf_file
# Ensure that only one URDF source is specified
specified_sources = [
src for src in (urdf, urdf_file, urdf_path) if src is not None
]
if len(specified_sources) > 1:
raise ValueError(
"'urdf', 'urdf_file', and 'urdf_path' cannot be given at the same time"
)
# urdf_path is treated as an alias for urdf_file for backward compatibility
urdf_input = urdf or urdf_file or urdf_path or r8_6_urdfpath()

Copilot uses AI. Check for mistakes.
- Remove unused default_urdf_path property from PR2 for consistency
- Add urdf_path parameter to R8_6 for backward compatibility
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 10 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

raise ValueError(
"'urdf' and 'urdf_file' cannot be given at the same time"
)
urdf_input = urdf or urdf_file or fetch_urdfpath()
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use of or chaining to select the URDF input could cause unexpected behavior if an empty string is intentionally passed as urdf or urdf_file. The empty string is falsy in Python, so urdf or urdf_file or fetch_urdfpath() would skip over an empty string input and fall through to the default path. Consider using explicit None checks instead: urdf if urdf is not None else (urdf_file if urdf_file is not None else fetch_urdfpath()).

Suggested change
urdf_input = urdf or urdf_file or fetch_urdfpath()
if urdf is not None:
urdf_input = urdf
elif urdf_file is not None:
urdf_input = urdf_file
else:
urdf_input = fetch_urdfpath()

Copilot uses AI. Check for mistakes.
raise ValueError(
"'urdf' and 'urdf_file' cannot be given at the same time"
)
urdf_input = urdf or urdf_file or kuka_urdfpath()
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use of or chaining to select the URDF input could cause unexpected behavior if an empty string is intentionally passed as urdf or urdf_file. The empty string is falsy in Python, so urdf or urdf_file or kuka_urdfpath() would skip over an empty string input and fall through to the default path. Consider using explicit None checks instead: urdf if urdf is not None else (urdf_file if urdf_file is not None else kuka_urdfpath()).

Suggested change
urdf_input = urdf or urdf_file or kuka_urdfpath()
urdf_input = (
urdf
if urdf is not None
else (urdf_file if urdf_file is not None else kuka_urdfpath())
)

Copilot uses AI. Check for mistakes.
raise ValueError(
"Only one of 'urdf', 'urdf_file', or 'urdf_path' can be specified"
)
urdf_input = urdf or urdf_file or urdf_path or r8_6_urdfpath()
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use of or chaining to select the URDF input could cause unexpected behavior if an empty string is intentionally passed. The empty string is falsy in Python, so this expression would skip over an empty string input and fall through to the next option or default path. Consider using explicit None checks instead: urdf if urdf is not None else (urdf_file if urdf_file is not None else (urdf_path if urdf_path is not None else r8_6_urdfpath())).

Suggested change
urdf_input = urdf or urdf_file or urdf_path or r8_6_urdfpath()
if urdf is not None:
urdf_input = urdf
elif urdf_file is not None:
urdf_input = urdf_file
elif urdf_path is not None:
urdf_input = urdf_path
else:
urdf_input = r8_6_urdfpath()

Copilot uses AI. Check for mistakes.
Comment on lines +25 to +30
if urdf is not None:
urdf_input = urdf
elif urdf_file is not None:
urdf_input = urdf_file
else:
self.load_urdf_file(file_obj=self.default_urdf_path)
urdf_input = self.default_urdf_path
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use of or chaining to select the URDF input could cause unexpected behavior if an empty string is intentionally passed. The empty string is falsy in Python, so this expression would skip over an empty string input and fall through to the next option or default path. Consider using explicit None checks with ternary operators to preserve the correct priority.

Copilot uses AI. Check for mistakes.
urdf_input : str
Either the URDF model description as a string, or the path to a
URDF file.
include_mimic_joints : bool, optional
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docstring states that include_mimic_joints is "optional" but should specify the default value explicitly in the parameter description. Based on the function signature, the default is True, but this should be documented as "bool, optional (default: True)" for clarity.

Suggested change
include_mimic_joints : bool, optional
include_mimic_joints : bool, optional (default: True)

Copilot uses AI. Check for mistakes.

def __init__(self, link_list=None, joint_list=None,
root_link=None):
root_link=None, urdf=None, include_mimic_joints=True):
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The __init__ method signature now includes new parameters urdf and include_mimic_joints but lacks documentation. Consider adding a docstring to document these parameters, especially since this is a public API method. The docstring should explain what urdf accepts (file path or URDF string), what include_mimic_joints does, and the defaults for all parameters.

Suggested change
root_link=None, urdf=None, include_mimic_joints=True):
root_link=None, urdf=None, include_mimic_joints=True):
"""Initialize a RobotModel instance.
Parameters
----------
link_list : sequence of Link, optional
Initial list of links to register in the model. If ``None``,
an empty list is used.
joint_list : sequence of Joint, optional
Initial list of joints to register in the model. If ``None``,
an empty list is used.
root_link : Link, optional
Root link of the robot model. If ``None``, a default or
inferred root link may be used depending on the implementation.
urdf : str, optional
URDF input used to build the robot model. This can be either a
file system path to a URDF file or a string containing URDF XML.
If ``None`` (the default), the model is not initialized from URDF.
include_mimic_joints : bool, optional
Whether to include mimic joints defined in the URDF when loading
the model. Defaults to ``True``.
"""

Copilot uses AI. Check for mistakes.
raise ValueError(
"'urdf' and 'urdf_file' cannot be given at the same time"
)
urdf_input = urdf or urdf_file or nextage_urdfpath()
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use of or chaining to select the URDF input could cause unexpected behavior if an empty string is intentionally passed as urdf or urdf_file. The empty string is falsy in Python, so urdf or urdf_file or nextage_urdfpath() would skip over an empty string input and fall through to the default path. Consider using explicit None checks instead: urdf if urdf is not None else (urdf_file if urdf_file is not None else nextage_urdfpath()).

Suggested change
urdf_input = urdf or urdf_file or nextage_urdfpath()
if urdf is not None:
urdf_input = urdf
elif urdf_file is not None:
urdf_input = urdf_file
else:
urdf_input = nextage_urdfpath()

Copilot uses AI. Check for mistakes.
raise ValueError(
"'urdf' and 'urdf_file' cannot be given at the same time"
)
urdf_input = urdf or urdf_file or panda_urdfpath()
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use of or chaining to select the URDF input could cause unexpected behavior if an empty string is intentionally passed as urdf or urdf_file. The empty string is falsy in Python, so urdf or urdf_file or panda_urdfpath() would skip over an empty string input and fall through to the default path. Consider using explicit None checks instead: urdf if urdf is not None else (urdf_file if urdf_file is not None else panda_urdfpath()).

Suggested change
urdf_input = urdf or urdf_file or panda_urdfpath()
urdf_input = (
urdf
if urdf is not None
else (urdf_file if urdf_file is not None else panda_urdfpath())
)

Copilot uses AI. Check for mistakes.
raise ValueError(
"'urdf' and 'urdf_file' cannot be given at the same time"
)
urdf_input = urdf or urdf_file or pr2_urdfpath()
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use of or chaining to select the URDF input could cause unexpected behavior if an empty string is intentionally passed as urdf or urdf_file. The empty string is falsy in Python, so urdf or urdf_file or pr2_urdfpath() would skip over an empty string input and fall through to the default path. Consider using explicit None checks instead: urdf if urdf is not None else (urdf_file if urdf_file is not None else pr2_urdfpath()).

Suggested change
urdf_input = urdf or urdf_file or pr2_urdfpath()
urdf_input = (
urdf
if urdf is not None
else (urdf_file if urdf_file is not None else pr2_urdfpath())
)

Copilot uses AI. Check for mistakes.
Comment on lines 1882 to 1884
logger.error("Attempting to load as URDF string instead.")
self.load_urdf(
urdf_input, include_mimic_joints=include_mimic_joints)
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fallback behavior in the exception handler is problematic. If a file path is provided and the file exists but loading fails (e.g., due to malformed XML), the code attempts to interpret the file path string as URDF content. This will likely fail in a confusing way since a file path is not valid URDF XML. Consider either removing the fallback entirely and re-raising the exception, or adding logic to check if the exception is due to file I/O vs parsing errors.

Suggested change
logger.error("Attempting to load as URDF string instead.")
self.load_urdf(
urdf_input, include_mimic_joints=include_mimic_joints)
# Do not attempt to interpret a file path as URDF XML content.
# Re-raise the original exception so callers see the real cause.
raise

Copilot uses AI. Check for mistakes.
- Add docstring to RobotModel.__init__ documenting new parameters
- Add default value to include_mimic_joints docstring in _load_from_urdf
- Remove problematic fallback that attempted to parse file path as URDF string
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 11 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

)
if urdf is not None and urdf_file is not None:
raise ValueError(
"'urdf' and 'urdf_file' cannot be given at the same time"
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parameter checking logic uses urdf is not None and urdf_file is not None which correctly checks for both parameters being explicitly provided. However, the error message could be more helpful by indicating which parameters were actually provided.

Suggested change
"'urdf' and 'urdf_file' cannot be given at the same time"
"'urdf' and 'urdf_file' cannot be given at the same time; "
"received both 'urdf' and 'urdf_file' arguments."

Copilot uses AI. Check for mistakes.
Comment on lines 1850 to 1852
URDF input used to build the robot model. This can be either a
file system path to a URDF file or a string containing URDF XML.
If None (the default), the model is not initialized from URDF.
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation for the urdf parameter should clarify the auto-detection behavior. Currently, it states "file system path to a URDF file or a string containing URDF XML" but doesn't explain that the method automatically detects which type it is using os.path.isfile(). This could be confusing if a URDF string happens to match an existing file path.

Suggested change
URDF input used to build the robot model. This can be either a
file system path to a URDF file or a string containing URDF XML.
If None (the default), the model is not initialized from URDF.
URDF input used to build the robot model. The value is interpreted
automatically: if it is a string for which ``os.path.isfile``
returns ``True``, it is treated as a file system path to a URDF
file; otherwise it is treated as a string containing URDF XML.
Note that if you pass a URDF XML string that happens to match an
existing file path, the file at that path will be loaded instead
of the inline XML. If None (the default), the model is not
initialized from URDF.

Copilot uses AI. Check for mistakes.
Comment on lines 14 to 31
def __init__(self, urdf=None, urdf_file=None):
warnings.warn(
"RobotModelFromURDF is deprecated. "
"Use RobotModel(urdf=...) or RobotModel.from_urdf(...) instead.",
DeprecationWarning,
stacklevel=2
)
if urdf is not None and urdf_file is not None:
raise ValueError(
"'urdf' and 'urdf_file' cannot be given at the same time"
)
if urdf:
self.load_urdf(urdf=urdf)
elif urdf_file:
self.load_urdf_file(file_obj=urdf_file)
if urdf is not None:
urdf_input = urdf
elif urdf_file is not None:
urdf_input = urdf_file
else:
self.load_urdf_file(file_obj=self.default_urdf_path)
urdf_input = self.default_urdf_path
super(RobotModelFromURDF, self).__init__(urdf=urdf_input)
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The deprecation of RobotModelFromURDF lacks test coverage to verify that the DeprecationWarning is properly raised when the class is instantiated. Consider adding a test to ensure the deprecation warning is emitted and that the class still functions correctly for backward compatibility.

Copilot uses AI. Check for mistakes.
Comment on lines 15 to 22
def __init__(self, urdf=None, urdf_file=None):
# For backward compatibility, support both urdf and urdf_file
if urdf is not None and urdf_file is not None:
raise ValueError(
"'urdf' and 'urdf_file' cannot be given at the same time"
)
urdf_input = urdf or urdf_file or fetch_urdfpath()
super(Fetch, self).__init__(urdf=urdf_input)
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new urdf and urdf_file parameters added to the Fetch.__init__ method lack test coverage. Consider adding tests to verify that custom URDF inputs can be provided and that the parameter validation works correctly (e.g., testing that providing both parameters raises a ValueError).

Copilot uses AI. Check for mistakes.
Comment on lines 16 to 23
def __init__(self, urdf=None, urdf_file=None):
# For backward compatibility, support both urdf and urdf_file
if urdf is not None and urdf_file is not None:
raise ValueError(
"'urdf' and 'urdf_file' cannot be given at the same time"
)
urdf_input = urdf or urdf_file or panda_urdfpath()
super(Panda, self).__init__(urdf=urdf_input)
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new urdf and urdf_file parameters added to the Panda.__init__ method lack test coverage. Consider adding tests to verify that custom URDF inputs can be provided and that the parameter validation works correctly (e.g., testing that providing both parameters raises a ValueError).

Copilot uses AI. Check for mistakes.
Comment on lines 8 to 19
def __init__(self, urdf=None, urdf_file=None, urdf_path=None):
# For backward compatibility, support urdf, urdf_file, and urdf_path
# urdf_path is treated as an alias for urdf_file for backward compatibility
specified_sources = [
src for src in (urdf, urdf_file, urdf_path) if src is not None
]
if len(specified_sources) > 1:
raise ValueError(
"Only one of 'urdf', 'urdf_file', or 'urdf_path' can be specified"
)
urdf_input = urdf or urdf_file or urdf_path or r8_6_urdfpath()
super(R8_6, self).__init__(urdf=urdf_input)
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new urdf, urdf_file, and urdf_path parameters added to the R8_6.__init__ method lack test coverage. Consider adding tests to verify that custom URDF inputs can be provided, that the parameter validation works correctly (e.g., testing that providing multiple parameters raises a ValueError), and that backward compatibility with the urdf_path parameter is maintained.

Copilot uses AI. Check for mistakes.
Comment on lines +1893 to +1899
if os.path.isfile(urdf_input):
with open(urdf_input, 'r') as f:
self.load_urdf_file(
file_obj=f, include_mimic_joints=include_mimic_joints)
else:
self.load_urdf(urdf_input,
include_mimic_joints=include_mimic_joints)
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The _load_from_urdf method uses os.path.isfile() to detect whether the input is a file path or URDF string. This approach has a potential edge case: if a URDF XML string happens to start with a valid file path that exists on the system, it will be incorrectly treated as a file. Consider checking for XML content (e.g., starts with '<?xml' or '<robot') before falling back to file path detection.

Suggested change
if os.path.isfile(urdf_input):
with open(urdf_input, 'r') as f:
self.load_urdf_file(
file_obj=f, include_mimic_joints=include_mimic_joints)
else:
self.load_urdf(urdf_input,
include_mimic_joints=include_mimic_joints)
# First, check if the input looks like URDF/XML content.
if isinstance(urdf_input, six.string_types):
stripped = urdf_input.lstrip()
if stripped.startswith('<?xml') or stripped.startswith('<robot'):
self.load_urdf(
urdf_input, include_mimic_joints=include_mimic_joints
)
return
# If it doesn't look like XML, fall back to treating it as a file path
# when possible; otherwise, pass it to load_urdf as before.
if isinstance(urdf_input, six.string_types) and os.path.isfile(urdf_input):
with open(urdf_input, 'r') as f:
self.load_urdf_file(
file_obj=f, include_mimic_joints=include_mimic_joints
)
else:
self.load_urdf(
urdf_input, include_mimic_joints=include_mimic_joints
)

Copilot uses AI. Check for mistakes.
Comment on lines 8 to 18
def __init__(self, urdf=None, urdf_file=None, urdf_path=None):
# For backward compatibility, support urdf, urdf_file, and urdf_path
# urdf_path is treated as an alias for urdf_file for backward compatibility
specified_sources = [
src for src in (urdf, urdf_file, urdf_path) if src is not None
]
if len(specified_sources) > 1:
raise ValueError(
"Only one of 'urdf', 'urdf_file', or 'urdf_path' can be specified"
)
urdf_input = urdf or urdf_file or urdf_path or r8_6_urdfpath()
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The R8_6 class accepts three different parameter names (urdf, urdf_file, urdf_path) while other robot models (Panda, Fetch, Kuka, PR2, Nextage) only accept two (urdf, urdf_file). This inconsistency in the API design could confuse users. Consider whether urdf_path is necessary for backward compatibility or if it should be standardized across all models.

Copilot uses AI. Check for mistakes.
Comment on lines 12 to 19
def __init__(self, urdf=None, urdf_file=None):
# For backward compatibility, support both urdf and urdf_file
if urdf is not None and urdf_file is not None:
raise ValueError(
"'urdf' and 'urdf_file' cannot be given at the same time"
)
urdf_input = urdf or urdf_file or kuka_urdfpath()
super(Kuka, self).__init__(urdf=urdf_input)
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new urdf and urdf_file parameters added to the Kuka.__init__ method lack test coverage. Consider adding tests to verify that custom URDF inputs can be provided and that the parameter validation works correctly (e.g., testing that providing both parameters raises a ValueError).

Copilot uses AI. Check for mistakes.
Comment on lines 20 to 27
def __init__(self, urdf=None, urdf_file=None):
# For backward compatibility, support both urdf and urdf_file
if urdf is not None and urdf_file is not None:
raise ValueError(
"'urdf' and 'urdf_file' cannot be given at the same time"
)
urdf_input = urdf or urdf_file or nextage_urdfpath()
super(Nextage, self).__init__(urdf=urdf_input)
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new urdf and urdf_file parameters added to the Nextage.__init__ method lack test coverage. Consider adding tests to verify that custom URDF inputs can be provided and that the parameter validation works correctly (e.g., testing that providing both parameters raises a ValueError).

Copilot uses AI. Check for mistakes.
- Use explicit None checks instead of or-chaining for URDF input selection
- Improve error message specificity in RobotModelFromURDF
- Update docstring to explain auto-detection behavior (XML vs file path)
- Add XML content detection (<?xml or <robot prefix) before file path check
- Standardize R8_6 API to match other robots (remove urdf_path parameter)
- Add test coverage for new URDF parameters and deprecation warning
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +16 to +28
def __init__(self, urdf=None, urdf_file=None):
# For backward compatibility, support both urdf and urdf_file
if urdf is not None and urdf_file is not None:
raise ValueError(
"'urdf' and 'urdf_file' cannot be given at the same time"
)
if urdf is not None:
urdf_input = urdf
elif urdf_file is not None:
urdf_input = urdf_file
else:
urdf_input = panda_urdfpath()
super(Panda, self).__init__(urdf=urdf_input)
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parameter handling logic for urdf and urdf_file is duplicated across all robot model classes (Panda, Fetch, Kuka, PR2, Nextage, R8_6). Consider extracting this into a helper method in the base RobotModel class or creating a decorator to reduce code duplication. This would make maintenance easier and ensure consistency across all robot models.

Copilot uses AI. Check for mistakes.
Comment on lines +1883 to +1915
def _load_from_urdf(self, urdf_input, include_mimic_joints=True):
"""Load URDF from a string or a file path.
Automatically detects if the input is a URDF string or a file path.
If the input looks like XML content (starts with '<?xml' or '<robot'),
it is treated as URDF XML string. Otherwise, if the input is an
existing file path, it is loaded as a file.
Parameters
----------
urdf_input : str
Either the URDF model description as a string, or the path to a
URDF file.
include_mimic_joints : bool, optional (default: True)
If True, mimic joints are included in the resulting
`RobotModel`'s `joint_list`.
"""
# First, check if the input looks like URDF/XML content
if isinstance(urdf_input, str):
stripped = urdf_input.lstrip()
if stripped.startswith('<?xml') or stripped.startswith('<robot'):
self.load_urdf(
urdf_input, include_mimic_joints=include_mimic_joints)
return

# If it doesn't look like XML, check if it's a file path
if os.path.isfile(urdf_input):
with open(urdf_input, 'r') as f:
self.load_urdf_file(
file_obj=f, include_mimic_joints=include_mimic_joints)
else:
self.load_urdf(urdf_input,
include_mimic_joints=include_mimic_joints)
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The _load_from_urdf method has incomplete input validation. If urdf_input is not a string (e.g., accidentally passed as a file object or other type), the code will skip the XML detection block (line 1901) and attempt to call os.path.isfile(urdf_input) on line 1909 with a non-string argument, which could cause a TypeError or unexpected behavior. Consider adding explicit type checking at the start of the method to provide a clearer error message if urdf_input is not a string.

Copilot uses AI. Check for mistakes.
Comment on lines +23 to +24
"'urdf' and 'urdf_file' cannot be given at the same time; "
"received both 'urdf' and 'urdf_file' arguments."
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message for providing both urdf and urdf_file is inconsistent with other robot model classes. All other robot models (Panda, Fetch, Kuka, PR2, Nextage, R8_6) use the simpler message "'urdf' and 'urdf_file' cannot be given at the same time" without the additional clause. Consider using the same error message format for consistency across the codebase.

Suggested change
"'urdf' and 'urdf_file' cannot be given at the same time; "
"received both 'urdf' and 'urdf_file' arguments."
"'urdf' and 'urdf_file' cannot be given at the same time"

Copilot uses AI. Check for mistakes.
@iory iory closed this Jan 5, 2026
@iory iory deleted the model branch January 5, 2026 12:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants