Skip to content

Commit 67704f1

Browse files
RSDK-3788 - add getkinematics functionality in arm (#335)
1 parent ef633e7 commit 67704f1

File tree

12 files changed

+394
-19
lines changed

12 files changed

+394
-19
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
"""
2+
This file registers the MyArm model with the Python SDK.
3+
"""
4+
5+
from viam.components.arm import Arm
6+
from viam.resource.registry import Registry, ResourceCreatorRegistration
7+
from .my_arm import MyArm
8+
9+
Registry.register_resource_creator(Arm.SUBTYPE, MyArm.MODEL, ResourceCreatorRegistration(MyArm.new))

examples/module/src/arm/my_arm.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import asyncio
2+
import os
3+
from typing import Any, ClassVar, Dict, Mapping, Optional, Self, Tuple
4+
from viam.components.arm import Arm, JointPositions, KinematicsFileFormat, Pose
5+
from viam.operations import run_with_operation
6+
from viam.proto.app.robot import ComponentConfig
7+
from viam.proto.common import ResourceName
8+
from viam.resource.base import ResourceBase
9+
from viam.resource.types import Model, ModelFamily
10+
11+
12+
class MyArm(Arm):
13+
# Subclass the Viam Arm component and implement the required functions
14+
MODEL: ClassVar[Model] = Model(ModelFamily("acme", "demo"), "myarm")
15+
16+
def __init__(self, name: str):
17+
# Starting position
18+
self.position = Pose(
19+
x=0,
20+
y=0,
21+
z=0,
22+
o_x=0,
23+
o_y=0,
24+
o_z=1,
25+
theta=0,
26+
)
27+
28+
# Starting joint positions
29+
self.joint_positions = JointPositions(values=[0, 0, 0, 0, 0, 0])
30+
self.is_stopped = True
31+
super().__init__(name)
32+
33+
@classmethod
34+
def new(cls, config: ComponentConfig, dependencies: Mapping[ResourceName, ResourceBase]) -> Self:
35+
arm = cls(config.name)
36+
return arm
37+
38+
async def get_end_position(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Pose:
39+
return self.position
40+
41+
@run_with_operation
42+
async def move_to_position(
43+
self,
44+
pose: Pose,
45+
extra: Optional[Dict[str, Any]] = None,
46+
**kwargs,
47+
):
48+
operation = self.get_operation(kwargs)
49+
50+
self.is_stopped = False
51+
52+
# Simulate the length of time it takes for the arm to move to its new position
53+
for x in range(10):
54+
await asyncio.sleep(1)
55+
56+
# Check if the operation is cancelled and, if it is, stop the arm's motion
57+
if await operation.is_cancelled():
58+
await self.stop()
59+
break
60+
61+
self.position = pose
62+
self.is_stopped = True
63+
64+
async def get_joint_positions(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> JointPositions:
65+
return self.joint_positions
66+
67+
@run_with_operation
68+
async def move_to_joint_positions(self, positions: JointPositions, extra: Optional[Dict[str, Any]] = None, **kwargs):
69+
operation = self.get_operation(kwargs)
70+
71+
self.is_stopped = False
72+
73+
# Simulate the length of time it takes for the arm to move to its new joint position
74+
for x in range(10):
75+
await asyncio.sleep(1)
76+
77+
# Check if the operation is cancelled and, if it is, stop the arm's motion
78+
if await operation.is_cancelled():
79+
await self.stop()
80+
break
81+
82+
self.joint_positions = positions
83+
self.is_stopped = True
84+
85+
async def stop(self, extra: Optional[Dict[str, Any]] = None, **kwargs):
86+
self.is_stopped = True
87+
88+
async def is_moving(self) -> bool:
89+
return not self.is_stopped
90+
91+
async def get_kinematics(self, **kwargs) -> Tuple[KinematicsFileFormat.ValueType, bytes]:
92+
dirname = os.path.dirname(__file__)
93+
filepath = os.path.join(dirname, "./xarm6_kinematics.json")
94+
with open(filepath, mode="rb") as f:
95+
file_data = f.read()
96+
return (KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, file_data)
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
{
2+
"name": "xArm6",
3+
"links": [
4+
{
5+
"id": "base",
6+
"parent": "world",
7+
"translation": {
8+
"x": 0,
9+
"y": 0,
10+
"z": 0
11+
}
12+
},
13+
{
14+
"id": "base_top",
15+
"parent": "waist",
16+
"translation": {
17+
"x": 0,
18+
"y": 0,
19+
"z": 267
20+
},
21+
"geometry": {
22+
"r": 50,
23+
"l": 320,
24+
"translation": {
25+
"x": 0,
26+
"y": 0,
27+
"z": 160
28+
}
29+
}
30+
},
31+
{
32+
"id": "upper_arm",
33+
"parent": "shoulder",
34+
"translation": {
35+
"x": 53.5,
36+
"y": 0,
37+
"z": 284.5
38+
},
39+
"geometry": {
40+
"x": 110,
41+
"y": 190,
42+
"z": 370,
43+
"translation": {
44+
"x": 0,
45+
"y": 0,
46+
"z": 135
47+
}
48+
}
49+
},
50+
{
51+
"id": "upper_forearm",
52+
"parent": "elbow",
53+
"translation": {
54+
"x": 77.5,
55+
"y": 0,
56+
"z": -172.5
57+
},
58+
"geometry": {
59+
"x": 100,
60+
"y": 190,
61+
"z": 250,
62+
"translation": {
63+
"x": 49.49,
64+
"y": 0,
65+
"z": -49.49
66+
},
67+
"orientation": {
68+
"type": "ov_degrees",
69+
"value": {
70+
"x": 0.707106,
71+
"y": 0,
72+
"z": -0.707106,
73+
"th": 0
74+
}
75+
}
76+
}
77+
},
78+
{
79+
"id": "lower_forearm",
80+
"parent": "forearm_rot",
81+
"translation": {
82+
"x": 0,
83+
"y": 0,
84+
"z": -170
85+
},
86+
"geometry": {
87+
"r": 45,
88+
"l": 285,
89+
"translation": {
90+
"x": 0,
91+
"y": -27.5,
92+
"z": -104.8
93+
},
94+
"orientation": {
95+
"type": "ov_degrees",
96+
"value": {
97+
"th": -90,
98+
"x": 0,
99+
"y": 0.2537568,
100+
"z": 0.9672615
101+
}
102+
}
103+
}
104+
},
105+
{
106+
"id": "wrist_link",
107+
"parent": "wrist",
108+
"translation": {
109+
"x": 76,
110+
"y": 0,
111+
"z": -97
112+
},
113+
"geometry": {
114+
"x": 150,
115+
"y": 100,
116+
"z": 135,
117+
"translation": {
118+
"x": 75,
119+
"y": 10,
120+
"z": -67.5
121+
}
122+
}
123+
},
124+
{
125+
"id": "gripper_mount",
126+
"parent": "gripper_rot",
127+
"translation": {
128+
"x": 0,
129+
"y": 0,
130+
"z": 0
131+
},
132+
"orientation": {
133+
"type": "ov_degrees",
134+
"value": {
135+
"x": 0,
136+
"y": 0,
137+
"z": -1,
138+
"th": 0
139+
}
140+
}
141+
}
142+
],
143+
"joints": [
144+
{
145+
"id": "waist",
146+
"type": "revolute",
147+
"parent": "base",
148+
"axis": {
149+
"x": 0,
150+
"y": 0,
151+
"z": 1
152+
},
153+
"max": 359,
154+
"min": -359
155+
},
156+
{
157+
"id": "shoulder",
158+
"type": "revolute",
159+
"parent": "base_top",
160+
"axis": {
161+
"x": 0,
162+
"y": 1,
163+
"z": 0
164+
},
165+
"max": 120,
166+
"min": -118
167+
},
168+
{
169+
"id": "elbow",
170+
"type": "revolute",
171+
"parent": "upper_arm",
172+
"axis": {
173+
"x": 0,
174+
"y": 1,
175+
"z": 0
176+
},
177+
"max": 10,
178+
"min": -225
179+
},
180+
{
181+
"id": "forearm_rot",
182+
"type": "revolute",
183+
"parent": "upper_forearm",
184+
"axis": {
185+
"x": 0,
186+
"y": 0,
187+
"z": -1
188+
},
189+
"max": 359,
190+
"min": -359
191+
},
192+
{
193+
"id": "wrist",
194+
"type": "revolute",
195+
"parent": "lower_forearm",
196+
"axis": {
197+
"x": 0,
198+
"y": 1,
199+
"z": 0
200+
},
201+
"max": 179,
202+
"min": -97
203+
},
204+
{
205+
"id": "gripper_rot",
206+
"type": "revolute",
207+
"parent": "wrist_link",
208+
"axis": {
209+
"x": 0,
210+
"y": 0,
211+
"z": -1
212+
},
213+
"max": 359,
214+
"min": -359
215+
}
216+
]
217+
}

examples/module/src/main.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import asyncio
22

33
from viam.module.module import Module
4+
from viam.components.arm import Arm
45

56
from .gizmo import Gizmo, MyGizmo
7+
from .arm import MyArm
68
from .summation import MySummationService, SummationService
79

810

@@ -14,6 +16,7 @@ async def main():
1416
module = Module.from_args()
1517
module.add_model_from_registry(Gizmo.SUBTYPE, MyGizmo.MODEL)
1618
module.add_model_from_registry(SummationService.SUBTYPE, MySummationService.MODEL)
19+
module.add_model_from_registry(Arm.SUBTYPE, MyArm.MODEL)
1720
await module.start()
1821

1922

examples/server/v1/components.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
BoardStatus,
4141
DigitalInterruptStatus,
4242
GeoPoint,
43+
KinematicsFileFormat,
4344
Orientation,
4445
Pose,
4546
PoseInFrame,
@@ -63,6 +64,7 @@ def __init__(self, name: str):
6364
)
6465
self.joint_positions = JointPositions(values=[0, 0, 0, 0, 0, 0])
6566
self.is_stopped = True
67+
self.kinematics = (KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, b"\x00\x01\x02")
6668
super().__init__(name)
6769

6870
async def get_end_position(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Pose:
@@ -90,6 +92,9 @@ async def stop(self, extra: Optional[Dict[str, Any]] = None, **kwargs):
9092
async def is_moving(self):
9193
return not self.is_stopped
9294

95+
async def get_kinematics(self, **kwargs) -> Tuple[KinematicsFileFormat.ValueType, bytes]:
96+
return self.kinematics
97+
9398

9499
class ExampleAudioInput(AudioInput):
95100
def __init__(self, name: str):

src/viam/components/arm/__init__.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import asyncio
22

3-
from viam.proto.common import Pose
3+
from viam.proto.common import KinematicsFileFormat, Pose
44
from viam.proto.component.arm import JointPositions
55
from viam.proto.component.arm import Status as ArmStatus
66
from viam.proto.robot import Status
@@ -14,16 +14,13 @@
1414
__all__ = [
1515
"Arm",
1616
"JointPositions",
17+
"KinematicsFileFormat",
1718
"Pose",
1819
]
1920

2021

2122
async def create_status(component: Arm) -> Status:
22-
(
23-
end_position,
24-
joint_positions,
25-
is_moving,
26-
) = await asyncio.gather(
23+
(end_position, joint_positions, is_moving,) = await asyncio.gather(
2724
component.get_end_position(),
2825
component.get_joint_positions(),
2926
component.is_moving(),

0 commit comments

Comments
 (0)