Create a Custom Arm Model
Built-in models each have a software driver in the RDK.
For example, the
ur5e’s driver is implemented in
ur.go in the RDK.
Each of these models must also include a kinematics file, which specifies the relative orientation of links and joints in its kinematic chain.
Each built-in driver in the RDK includes a corresponding kinematics file located in the same directory as the driver.
For example, the
ur5e’s kinematics file,
ur5e.json, is provided in the RDK in the same directory as its driver,
See Arm Configuration for the current list of built-in models the RDK provides.
If you have a robot arm that is not already supported by the RDK, create a module that provides a customized model for your arm to program and control it with the arm API, or use it with services like Motion, just as you would with a built-in model.
See Modular Resources for more information.
Get your arm’s kinematics file
The way arms move through space is more complicated than Viam’s other components. Because of this, an arm, unlike other components, requires a kinematic configuration file describing its geometry. This provides the necessary information for the frame system service and built-in motion service to work with the arm.
Find a pre-built kinematics file:
viam-serverwill work with
URDF(United Robot Description Format) kinematics files, which are currently the standard for ROS drivers. You can find URDF“robot descriptions” for many industrial robot arm models on GitHub that are compatible with the Viam platform.
Create your own kinematics file:
- Follow the instructions on Configure Complex Kinematic Chains to write a file detailing the geometry of your arm.
- Use the Spatial Vector Algebra (SVA) kinematic parameter type.
- Define the parameters in a .json file.
- Follow the frame system’s guide to Configure a Reference Frame when working out the relative orientations of the
linkson your arm. You can view existing examples of the SVA and JSON format in Viam’s built-in arm drivers.
Create a new directory.
Give it the name your want you call your custom model of arm.
Save the JSON as
Create a custom arm model as a modular resource
To create a custom arm model, code a module in Python with the module support libraries provided by Viam’s SDKs:
Code a new resource model implementing all methods the Viam RDK requires in the API definition of its built-in subtype,
rdk:component:arm, which is available for reference on GitHub. Import your custom model and API into the main program and register the new resource model with the Python SDK.
Code a main program that starts the module after adding your desired resources from the registry. This main program is the “entry point” to your module.
Compile or package the module into a single executable that can receive a socket argument from
viam-server, open the socket, and start the module at the entry point.
Code a new resource model
This guide uses Viam’s Python SDK to implement a custom arm module, but if you want to use the Go Client SDK, you can. Follow this guide and select Go on the code samples to learn how to code a modular arm in Go.
Save the following two files,
my_modular_arm.pyimplements a custom model of the arm component built-in resource,
Click to view sample code from
import asyncio import os from typing import Any, ClassVar, Dict, Mapping, Optional, Tuple from typing_extensions import Self from viam.components.arm import Arm, JointPositions, KinematicsFileFormat, Pose from viam.operations import run_with_operation from viam.proto.app.robot import ComponentConfig from viam.proto.common import ResourceName from viam.resource.base import ResourceBase from viam.resource.types import Model, ModelFamily class MyModularArm(Arm): # Subclass the Viam Arm component and implement the required functions MODEL: ClassVar[Model] = Model(ModelFamily("acme", "demo"), "myarm") def __init__(self, name: str): # Starting joint positions self.joint_positions = JointPositions(values=[0, 0, 0, 0, 0, 0]) super().__init__(name) @classmethod def new(cls, config: ComponentConfig, dependencies: Mapping[ResourceName, ResourceBase]) -> Self: arm = cls(config.name) return arm async def get_end_position(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Pose: raise NotImplementedError() async def move_to_position(self, pose: Pose, extra: Optional[Dict[str, Any]] = None, **kwargs): raise NotImplementedError() async def get_joint_positions(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> JointPositions: return self.joint_positions @run_with_operation async def move_to_joint_positions(self, positions: JointPositions, extra: Optional[Dict[str, Any]] = None, **kwargs): operation = self.get_operation(kwargs) self.is_stopped = False # Simulate the length of time it takes for the arm to move to its new joint position for x in range(10): await asyncio.sleep(1) # Check if the operation is cancelled and, if it is, stop the arm's motion if await operation.is_cancelled(): await self.stop() break self.joint_positions = positions self.is_stopped = True async def stop(self, extra: Optional[Dict[str, Any]] = None, **kwargs): self.is_stopped = True async def is_moving(self) -> bool: return not self.is_stopped async def get_kinematics(self, **kwargs) -> Tuple[KinematicsFileFormat.ValueType, bytes]: dirname = os.path.dirname(__file__) filepath = os.path.join(dirname, "./xarm6_kinematics.json") with open(filepath, mode="rb") as f: file_data = f.read() return (KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, file_data)
my_modular_armcustom model and API helper functions with the Python SDK.
Click to view sample code from
from viam.components.arm import Arm from viam.resource.registry import Registry, ResourceCreatorRegistration from .my_modular_arm import MyModularArm Registry.register_resource_creator(Arm.SUBTYPE, MyModularArm.MODEL, ResourceCreatorRegistration(MyModularArm.new))
The Python code for the custom model (
Code a main entry point program
myarm custom model and API helper functions from the registry.
Click to view sample code from
import asyncio from viam.module.module import Module from viam.components.arm import Arm from .my_modular_arm import MyModularArm async def main(): """This function creates and starts a new module, after adding all desired resources. Resources must be pre-registered. For an example, see the `__init__.py` file. """ module = Module.from_args() module.add_model_from_registry(Arm.SUBTYPE, MyModularArm.MODEL) await module.start() if __name__ == "__main__": asyncio.run(main())
You must define all functions belonging to a built-in resource subtype’s API if defining a new model. Otherwise, the class won’t instantiate.
The best practice with the Python SDK is to put
pass or raise an
NotImplementedError() in the body of functions you don’t want to implement.
Compile the module into an executable
To add a module to the configuration of your robot, you need to have an executable that runs your module when executed, can take a local socket as a command line argument, and cleanly exits when sent a termination signal.
Your options for completing this step are flexible, as this file does not need to be in a raw binary format.
One option is to create and save a new shell script (
#!/bin/sh cd `dirname $0` exec python3 -m <your-module-directory-name>.<main-program-filename-without-extension> $@
This script uses exec to be able to ensure that termination signals reach the python process. If you omit this, be sure to handle the forwarding of termination signals accordingly.
To make this shell script executable, run the following command in your terminal:
sudo chmod +x <FILEPATH>/<FILENAME>
Ensure any dependencies for your module (including the Python SDK) are installed on your robot’s computer.
Your executable will be run by
viam-server as root, so dependencies need to be available to the root user.
Configure the module and modular resource on your robot
Follow these configuration instructions to add your custom resource to your robot.
Have questions, or want to meet other people working on robots? Join our Community Discord.
Was this page helpful?
Glad to hear it! If you have any other feedback please let us know:
We're sorry about that. To help us improve, please tell us what we can do better: