Robot Modeling

moro represents serial robotic manipulators through the Robot class.

A robot model is created from its Denavit-Hartenberg parameters and joint types. Once the model has been defined, the same Robot object can be used throughout the library to compute forward kinematics, Jacobians, inverse kinematics, dynamics, and visualizations.

This section focuses on how to define and inspect a robot model. The mathematical details of the Denavit-Hartenberg convention are covered separately in the Theory section.

Creating a serial robot

A robot is created by passing one Denavit-Hartenberg row for each joint:

from moro import Robot

For example, a planar two-link manipulator with two revolute joints can be defined as:

from moro.abc import q1, q2, l1, l2

robot = Robot(
    (l1, 0, 0, q1, "r"),
    (l2, 0, 0, q2, "r"),
)

Each row describes the relative transformation between two consecutive frames in the serial kinematic chain.

The number of rows passed to Robot determines the number of degrees of freedom of the manipulator.

Denavit-Hartenberg rows

Each Denavit-Hartenberg row must contain either four or five elements.

The four-parameter form is:

(a_i, alpha_i, d_i, theta_i)

The five-parameter form additionally specifies the joint type:

(a_i, alpha_i, d_i, theta_i, joint_type)

The parameters correspond to the classical Denavit-Hartenberg convention:

Position

Parameter

Description

1

\(a_i\)

Link length

2

\(\alpha_i\)

Link twist

3

\(d_i\)

Link offset

4

\(\theta_i\)

Joint angle

5

joint_type

Revolute or prismatic joint

If the joint type is omitted, the joint is assumed to be revolute.

For example, these two definitions are equivalent:

robot1 = Robot(
    (l1, 0, 0, q1),
    (l2, 0, 0, q2),
)

and:

robot2 = Robot(
    (l1, 0, 0, q1, "r"),
    (l2, 0, 0, q2, "r"),
)

Numeric angular parameters must be given in radians.

For a detailed explanation of the frame assignment and the meaning of each parameter, see Theory → Denavit-Hartenberg Convention.

Revolute and prismatic joints

moro currently supports two joint types:

  • "r" for revolute joints;

  • "p" for prismatic joints.

For a revolute joint, the joint variable is taken from the \(\theta_i\) parameter.

For example:

from moro.abc import q1

robot = Robot(
    (1, 0, 0, q1, "r"),
)

Here, q1 represents the rotational joint coordinate.

For a prismatic joint, the joint variable is taken from the \(d_i\) parameter:

robot = Robot(
    (0, 0, q1, 0, "p"),
)

Here, q1 represents the translational displacement of the joint.

Joint type identifiers are case-insensitive, so "R" and "P" are also accepted. For consistency, lowercase "r" and "p" are recommended in user code and documentation.

Using symbolic parameters

Robot models can contain symbolic parameters, which makes it possible to derive kinematic and dynamic expressions before assigning numerical values.

Using predefined symbols

For convenience, moro.abc provides several commonly used symbolic variables:

from moro.abc import q1, q2, l1, l2

These can be used directly when constructing a robot:

robot = Robot(
    (l1, 0, 0, q1, "r"),
    (l2, 0, 0, q2, "r"),
)

The joint variables available from moro.abc, such as q1 and q2, are time-dependent symbolic quantities. This makes them suitable for models that may later be reused for dynamic analysis.

Defining your own symbols

Using moro.abc is optional. You can define your own parameters directly with SymPy.

For example:

from sympy import symbols
from sympy.physics.mechanics import dynamicsymbols

l1, l2 = symbols("l1 l2", positive=True)
q1, q2 = dynamicsymbols("q1 q2")

These symbols can then be used normally:

robot = Robot(
    (l1, 0, 0, q1, "r"),
    (l2, 0, 0, q2, "r"),
)

This approach is useful when a model requires custom variable names or assumptions.

Inspecting the robot model

Once a robot has been created, several properties can be used to inspect its structure.

Denavit-Hartenberg parameters

The original Denavit-Hartenberg parameters are available through:

robot.dh_parameters

For the previous 2R manipulator, this returns one tuple for each row used to construct the robot.

A tabular representation is available through:

robot.dh_table

This is useful for checking the model before performing further calculations.

Joint types

The joint types can be inspected with:

robot.joint_types

For a 2R manipulator:

["r", "r"]

For a mixed revolute-prismatic manipulator, the result could instead be:

["r", "p"]

Individual transformations

Robot also stores the homogeneous transformation associated with each Denavit-Hartenberg row.

They can be inspected through:

robot.Ts

The resulting list contains the relative transformation matrices between consecutive frames.

More detailed operations involving these transformations are covered in Forward Kinematics.

Joint variables and degrees of freedom

The number of degrees of freedom is available through:

robot.dof

For the planar 2R example:

robot.dof
# 2

The joint variables detected from the robot definition are available through:

robot.qs

For the same robot:

robot.qs
# [q1, q2]

The user does not need to provide the joint variables separately. moro determines them from the joint types and the corresponding Denavit-Hartenberg parameters.

For a revolute joint, the joint coordinate is obtained from \(\theta_i\), while for a prismatic joint it is obtained from \(d_i\).

Joint limits

Each Robot model also stores one pair of limits for every joint.

These can be inspected through:

robot.joint_limits

By default, moro assigns:

  • \((-\pi, \pi)\) to revolute joints;

  • \((0, 1000)\) to prismatic joints.

For example:

from sympy import pi

robot = Robot(
    (1, 0, 0, q1, "r"),
    (0, 0, q2, 0, "p"),
)

robot.joint_limits

corresponds to:

[(-pi, pi), (0, 1000)]

These default values are convenience ranges rather than physical limits of a particular mechanism.

For a real robot, the limits should normally be replaced with values that represent the actual admissible motion.

For example:

robot.joint_limits = [
    (-pi / 2, pi / 2),
    (0, 0.5),
]

The number of limit pairs must match the number of degrees of freedom, and each joint limit must be specified as a pair:

(lower_limit, upper_limit)

Angular limits are expressed in radians.

Joint limits become especially relevant when solving inverse kinematics problems, where they can be used to constrain the admissible joint configurations.

A mixed revolute-prismatic example

Consider a two-degree-of-freedom manipulator with one revolute joint followed by one prismatic joint:

from moro import Robot
from moro.abc import q1, q2, l1

robot = Robot(
    (l1, 0, 0, q1, "r"),
    (0, 0, q2, 0, "p"),
)

The model can then be inspected with:

robot.dof

which returns:

2

The joint types are:

robot.joint_types
["r", "p"]

and the joint variables are:

robot.qs

corresponding to:

[q1, q2]

The Denavit-Hartenberg table can be inspected with:

robot.dh_table

and the default joint limits can be checked with:

robot.joint_limits

This same model can then be passed directly to the kinematics, inverse kinematics, dynamics, and visualization workflows provided by moro.

Notes and limitations

The current robot modeling capabilities in moro are centered on serial manipulators.

At present:

  • robot geometry is described using the classical Denavit-Hartenberg convention;

  • revolute and prismatic joints are supported;

  • both symbolic and numerical Denavit-Hartenberg parameters can be used;

  • numeric angular quantities are expressed in radians;

  • one joint coordinate is associated with each Denavit-Hartenberg row;

  • URDF and other robot-description formats are not currently supported;

  • branched, parallel, or closed-loop kinematic structures are outside the current scope.

Creating a Robot primarily defines its kinematic structure.

Dynamic properties such as link masses, centers of mass, inertia tensors, and the gravity vector are configured separately when dynamic modeling is required. These quantities are introduced in the Dynamics section of the User Guide.

See also

  • Theory → Denavit-Hartenberg Convention — mathematical background for the robot description used by moro.

  • Forward Kinematics — compute transformations and poses throughout the robot chain.

  • Jacobians — compute geometric Jacobians for the end-effector and other points.

  • Inverse Kinematics — solve for joint configurations subject to robot constraints.

  • Dynamics — add physical parameters and derive the robot dynamic model.

  • Visualization — plot and animate robot configurations.

  • API Reference → Robot — complete reference for the Robot class.