Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(RPS-1027): separated plan and execute in motion group #16

Merged
merged 1 commit into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions examples/02_plan_and_execute.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
from math import pi
import asyncio

from nova.core.movement_controller import move_forward


async def main():
nova = Nova()
Expand Down Expand Up @@ -39,7 +37,8 @@ async def main():
jnt(home_joints),
]

await motion_group.run(actions, tcp=tcp, movement_controller=move_forward)
joint_trajectory = await motion_group.plan(actions, tcp)
await motion_group.execute(joint_trajectory, tcp, actions=actions)


if __name__ == "__main__":
Expand Down
14 changes: 3 additions & 11 deletions examples/03_move_and_set_ios.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ async def main():
nova = Nova()
cell = nova.cell()
controllers = await cell.controllers()
controller = controllers[0]

# Define a home position
home_joints = (0, -pi / 4, -pi / 4, -pi / 4, pi / 4, 0)

# Connect to the controller and activate motion groups
async with controllers[0] as motion_group:
async with controller[0] as motion_group:
tcp_names = await motion_group.tcp_names()
tcp = tcp_names[0]

Expand All @@ -32,16 +33,7 @@ async def main():
]

# io_value = await controller.read_io("digital_out[0]")

# plan_response = await motion_group.plan(trajectory, tcp="Flange")
# print(plan_response)

def print_motion(motion):
print(motion)

await motion_group.run(actions, tcp=tcp, initial_movement_consumer=print_motion)
await motion_group.run(actions, tcp=tcp)
await motion_group.run(ptp(target_pose), tcp=tcp)
await motion_group.plan_and_execute(actions, tcp)


if __name__ == "__main__":
Expand Down
4 changes: 2 additions & 2 deletions examples/04_move_multiple_robots.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from nova import Nova, Controller, speed_up_movement_controller
from nova import Nova, Controller
from nova.actions import ptp, jnt
from math import pi
import asyncio
Expand All @@ -15,7 +15,7 @@ async def move_robot(controller: Controller):
target_pose = current_pose @ (100, 0, 0, 0, 0, 0)
actions = [jnt(home_joints), ptp(target_pose), jnt(home_joints)]

await motion_group.run(actions, tcp=tcp, movement_controller=speed_up_movement_controller)
await motion_group.plan_and_execute(actions, tcp)


async def main():
Expand Down
8 changes: 4 additions & 4 deletions examples/05_selection_motion_group_activation.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ async def move_robot(motion_group: MotionGroup, tcp: str):
ptp(home_pose),
]

await motion_group.run(actions, tcp=tcp)
await motion_group.plan_and_execute(actions, tcp=tcp)


async def main():
Expand All @@ -37,7 +37,7 @@ async def main():
kuka = await cell.controller("kuka")
tcp = "Flange"

flange_state = await ur[0].get_state(tcp=tcp)
flange_state = await ur[0].get_state(tcp)
print(flange_state)

# activate all motion groups
Expand All @@ -46,11 +46,11 @@ async def main():

# activate motion group 0
async with ur.motion_group(0) as mg_0:
await move_robot(mg_0)
await move_robot(mg_0, tcp)

# activate motion group 0
async with ur[0] as mg_0:
await move_robot(mg_0)
await move_robot(mg_0, tcp)

# activate motion group 0 from two different controllers
async with ur[0] as ur_0_mg, kuka[0] as kuka_0_mg:
Expand Down
71 changes: 46 additions & 25 deletions nova/core/motion_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,49 +45,66 @@ def current_motion(self) -> str:
# raise ValueError("No MotionId attached. There is no planned motion available.")
return self._current_motion

async def plan(self, actions: list[Action], tcp: str) -> wb.models.JointTrajectory:
current_joints = await self.joints(tcp=tcp)
robot_setup = await self._get_optimizer_setup(tcp=tcp)
motion_commands = CombinedActions(items=actions).to_motion_command()
async def plan(self, actions: list[Action] | Action, tcp: str) -> wb.models.JointTrajectory:
"""Plan a trajectory for the given actions

Args:
actions (list[Action] | Action): The actions to be planned. Can be a single action or a list of actions.
Only motion actions are considered for planning.
tcp (str): The identifier of the tool center point (TCP)

Returns:
wb.models.JointTrajectory: The planned joint trajectory
"""
if not isinstance(actions, list):
actions = [actions]

if len(actions) == 0:
raise ValueError("No actions provided")

motion_commands = CombinedActions(items=actions).to_motion_command()
joints = await self.joints()
robot_setup = await self._get_optimizer_setup(tcp=tcp)
request = wb.models.PlanTrajectoryRequest(
robot_setup=robot_setup,
motion_group=self.motion_group_id,
start_joint_position=current_joints.joints,
motion_commands=motion_commands,
start_joint_position=joints.joints,
tcp=tcp,
)

motion_api_client = self._api_gateway.motion_api
plan_response = await motion_api_client.plan_trajectory(
plan_trajectory_response = await self._motion_api_client.plan_trajectory(
cell=self._cell, plan_trajectory_request=request
)

if isinstance(
plan_response.response.actual_instance, wb.models.PlanTrajectoryFailedResponse
plan_trajectory_response.response.actual_instance,
wb.models.PlanTrajectoryFailedResponse,
):
failed_response = plan_response.response.actual_instance
raise PlanTrajectoryFailed(failed_response)

return plan_response.response.actual_instance
raise PlanTrajectoryFailed(plan_trajectory_response.response.actual_instance)
return plan_trajectory_response.response.actual_instance

async def run(
async def execute(
self,
actions: list[Action] | Action,
joint_trajectory: wb.models.JointTrajectory,
tcp: str,
actions: list[Action] | Action | None = None,
# collision_scene: dts.CollisionScene | None,
response_rate_in_ms: int = 200,
movement_controller: MovementController = move_forward,
initial_movement_consumer: InitialMovementConsumer | None = None,
):
if not isinstance(actions, list):
actions = [actions]

if len(actions) == 0:
raise ValueError("No actions provided")
"""Execute a planned motion

# PLAN MOTION
joint_trajectory = await self.plan(actions, tcp)
Args:
joint_trajectory (wb.models.JointTrajectory): The planned joint trajectory
tcp (str): The identifier of the tool center point (TCP)
actions (list[Action] | Action | None): The actions to be executed. Defaults to None.
movement_controller (MovementController): The movement controller to be used. Defaults to move_forward
initial_movement_consumer (InitialMovementConsumer): A consumer for the initial movement
"""
if actions is None:
actions = []
elif not isinstance(actions, list):
actions = [actions]

# LOAD MOTION
load_plan_response = await self._load_planned_motion(joint_trajectory, tcp)
Expand All @@ -96,8 +113,8 @@ async def run(
number_of_joints = await self._get_number_of_joints()
joints_velocities = [MAX_JOINT_VELOCITY_PREPARE_MOVE] * number_of_joints
movement_stream = await self.move_to_start_position(joints_velocities, load_plan_response)
if initial_movement_consumer is not None:
async for move_to_response in movement_stream:
async for move_to_response in movement_stream:
if initial_movement_consumer is not None:
initial_movement_consumer(move_to_response)

# EXECUTE MOTION
Expand All @@ -107,6 +124,10 @@ async def run(
_movement_controller = movement_controller(movement_controller_context)
await self._api_gateway.motion_api.execute_trajectory(self._cell, _movement_controller)

async def plan_and_execute(self, actions: list[Action] | Action, tcp: str):
joint_trajectory = await self.plan(actions, tcp)
await self.execute(joint_trajectory, tcp, actions=actions)

async def _get_number_of_joints(self) -> int:
spec = await self._api_gateway.motion_group_infos_api.get_motion_group_specification(
cell=self._cell, motion_group=self.motion_group_id
Expand Down
5 changes: 3 additions & 2 deletions tests/core/test_motion_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ async def test_motion_group():

async with controller:
motion_group = controller[0]
state = await motion_group.get_state("Flange")
tcp = "Flange"
state = await motion_group.get_state(tcp)
print(state)

await motion_group.run(actions=actions, tcp="Flange")
await motion_group.plan_and_execute(actions, tcp)
assert True
Loading