Source code for urdfgenpy.joint
#!/usr/bin/env python3
"""Joint elements for URDF robot descriptions."""
# --- std
from dataclasses import dataclass, field
from typing import Literal
# --- user
from .elements import Origin
# Reference: https://urdfpy.readthedocs.io/en/latest/generated/urdfpy.Joint.html
JointType = Literal["fixed", "prismatic", "revolute", "continuous", "planar", "floating"]
[docs]
@dataclass
class JointLimit:
"""Motion limits for a joint.
Args:
lower: Lower position limit (rad or m).
upper: Upper position limit (rad or m).
effort: Maximum effort (N.m or N).
velocity: Maximum velocity (rad/s or m/s).
"""
lower: float = 0.0
upper: float = 0.0
effort: float = 0.0
velocity: float = 0.0
[docs]
def to_xml(self, indent: str = "") -> str:
"""Return the '<limit>' XML element string.
Args:
indent: Leading whitespace prepended to the element.
Returns:
A single-line '<limit ... />' XML string.
"""
return (
f'{indent}<limit lower="{self.lower}" upper="{self.upper}" '
f'effort="{self.effort}" velocity="{self.velocity}"/>'
)
[docs]
@dataclass
class JointDynamics:
"""Physical damping and friction for a joint.
Args:
damping: Viscous damping coefficient (N.m.s/rad).
friction: Coulomb friction coefficient (N.m).
"""
damping: float = 0.0
friction: float = 0.0
[docs]
def to_xml(self, indent: str = "") -> str:
"""Return the '<dynamics>' XML element string.
Args:
indent: Leading whitespace prepended to the element.
Returns:
A single-line '<dynamics ... />' XML string.
"""
return f'{indent}<dynamics damping="{self.damping}" friction="{self.friction}"/>'
[docs]
@dataclass
class JointMimic:
"""Constraint that mirrors another joint's motion.
Args:
joint: Name of the joint to mimic.
multiplier: Scale factor applied to the mimicked joint's value.
offset: Constant offset added after scaling.
"""
joint: str
multiplier: float = 1.0
offset: float = 0.0
[docs]
def to_xml(self, indent: str = "") -> str:
"""Return the '<mimic>' XML element string.
Args:
indent: Leading whitespace prepended to the element.
Returns:
A single-line '<mimic ... />' XML string.
"""
return (
f'{indent}<mimic joint="{self.joint}" '
f'multiplier="{self.multiplier}" offset="{self.offset}"/>'
)
[docs]
@dataclass
class Joint:
"""
Kinematic connection between two links.
Args:
name: Unique joint name.
joint_type: One of "fixed", "prismatic", "revolute", "continuous", "planar" or "floating".
parent: Name of the parent link.
child: Name of the child link.
origin: Transform from parent link frame to joint frame.
axis: Unit vector defining the joint axis (ignored for 'fixed'/'floating').
limit: Optional motion limits (:class:'JointLimit').
dynamics: Optional damping/friction (:class:'JointDynamics').
mimic: Optional mimic constraint (:class:'JointMimic').
"""
name: str
joint_type: JointType
parent: str
child: str
origin: Origin = field(default_factory=Origin)
axis: tuple[float, float, float] = (1.0, 0.0, 0.0)
limit: JointLimit | None = None
dynamics: JointDynamics | None = None
mimic: JointMimic | None = None
[docs]
def to_xml(self, indent: str = "") -> str:
"""Return the '<joint>' XML block string.
Args:
indent: Leading whitespace prepended to each line.
Returns:
A multi-line '<joint>...</joint>' XML string.
"""
lines = [f'{indent}<joint name="{self.name}" type="{self.joint_type}">']
lines.append(self.origin.to_xml(indent + " "))
lines.append(f'{indent} <parent link="{self.parent}"/>')
lines.append(f'{indent} <child link="{self.child}"/>')
if self.joint_type not in ("fixed", "floating"):
ax, ay, az = self.axis
lines.append(f'{indent} <axis xyz="{ax} {ay} {az}"/>')
if self.limit is not None:
lines.append(self.limit.to_xml(indent + " "))
if self.dynamics is not None:
lines.append(self.dynamics.to_xml(indent + " "))
if self.mimic is not None:
lines.append(self.mimic.to_xml(indent + " "))
lines.append(f"{indent}</joint>")
return "\n".join(lines)