-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathcontrol.py
More file actions
201 lines (170 loc) · 7.57 KB
/
Copy pathcontrol.py
File metadata and controls
201 lines (170 loc) · 7.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
"""Controller module."""
import logging
from typing import Dict, Tuple
import numpy as np
from envs.rotations import embedding2mat, mat2quat, quat2mat
from optim.utils.utils import filter_info
from optim.core.optimizer import Optimizer
from optim.objective import create_cube_objective
from optim.geometry import get_geometry, Cube
from optim.grippers import get_gripper
logger = logging.getLogger(__name__)
class Controller:
"""Controller class for controlling the agent with the optimized grasp configuration."""
GRIPPER_EPS = 1e-3
r_T_v = np.array([[0., 1., 0., 0.], [0., 0., 1., 0.], [1., 0., 0., 0.], [0., 0., 0., 1.]])
def __init__(self):
"""Initialize the controller and its optimizer."""
self.opt = Optimizer()
self.opt_pos = None
self.opt_orient = None
self.opt_grasp = None
self._is_reached = False
self._is_lowered = False
self._is_grasped = False
self.geom = None
def reset(self):
"""Reset the controller."""
self._is_reached = False
self._is_lowered = False
self._is_grasped = False
self.opt_pos = None
self.opt_orient = None
self.opt_grasp = None
self.opt.reset()
def __call__(self, state: np.ndarray, goal: np.ndarray) -> np.ndarray:
"""Compute the control input for the environment given the current state and goal.
Args:
state: Current (unnormalized) environment state.
goal: Current (unnormalized) environment goal.
Returns:
The control vector.
Raises:
AssertionError: No optimal grasp is registered.
"""
assert self.opt_grasp is not None, "No optimal grasp registered"
gripper_state = state[9:11]
w_T_r = np.eye(4) # Robot transformation
w_T_r[:3, :3] = embedding2mat(state[3:9])
w_T_r[:3, 3] = state[:3]
w_T_g = np.eye(4) # Geometry transformation
w_T_g[:3, :3] = embedding2mat(state[20:26])
w_T_g[:3, 3] = state[14:17]
w_T_rdes = w_T_g @ self.g_T_r
reach_offset = np.zeros((4, 4))
reach_offset[:3, 3] = [0., 0, 0.055]
if self._check_reached(w_T_r, w_T_rdes + reach_offset):
logger.debug("reaching is complete")
self._is_reached = True
if self._check_reached(w_T_r, w_T_rdes):
logger.debug("lowering is complete")
self._is_lowered = True
if not self._is_reached:
logger.debug("reaching phase")
pos_ctrl, rot_ctrl = self._compute_ctrl(w_T_r, w_T_rdes + reach_offset)
gripper_ctrl = self.opt_grasp + self.GRIPPER_EPS
elif not self._is_lowered:
logger.debug("lowering phase")
pos_ctrl, rot_ctrl = self._compute_ctrl(w_T_r, w_T_rdes)
gripper_ctrl = self.opt_grasp + self.GRIPPER_EPS
elif not self._is_grasped:
logger.debug("grasping phase")
pos_ctrl, rot_ctrl = self._compute_ctrl(w_T_r, w_T_rdes)
gripper_ctrl = -np.ones(1)
if np.mean(gripper_state) < 0.03:
self._is_grasped = True
else:
logger.debug("goal reaching phase")
w_T_goal = w_T_rdes.copy()
w_T_goal[:3, 3] = goal
pos_ctrl, rot_ctrl = self._compute_ctrl(w_T_g, w_T_goal)
gripper_ctrl = -np.ones(1)
return np.concatenate((pos_ctrl, rot_ctrl, gripper_ctrl))
def _compute_ctrl(self, w_T_r: np.ndarray, w_T_d: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
"""Compute the pose control input given the current pose and a target pose.
Args:
w_T_r: Current pose as homogeneous transformation matrix.
w_T_d: Desired pose as homogeneous transformation matrix.
Returns:
The position control and the orientation control as flattened rotation matrix.
"""
dx = (w_T_d[:3, 3] - w_T_r[:3, 3])
dx = dx / (np.linalg.norm(dx) + 1e-2)
dr = (w_T_d @ self.r_T_v)[:3, :3]
return dx, dr.flatten()
def _check_reached(self, w_T_r: np.ndarray, w_T_d: np.ndarray) -> bool:
"""Check if the current pose is within target tolerances of the desired pose.
Args:
w_T_r: Current pose as homogeneous transformation matrix.
w_T_d: Desired pose as homogeneous transformation matrix.
Returns:
True if the pose is within tolerances, else False.
"""
dx = w_T_r[:3, 3] - w_T_d[:3, 3]
orient, des_orient = mat2quat(w_T_r[:3, :3]), mat2quat(w_T_d[:3, :3])
dq = 2 * np.arccos(np.clip(np.abs(np.sum(orient * des_orient, axis=-1)), -1, 1))
pos_reached, orient_reached = np.linalg.norm(dx) < 5e-3, dq < 1e-2
return pos_reached and orient_reached
def optimize_grasp(self, info: Dict) -> np.ndarray:
"""Optimize the grasp configuration and set it as the current target for the controller.
Args:
info: Contact information dictionary.
Returns:
The optimized configuration.
Raises:
RuntimeError: The optimization has failed to converge.
"""
xinit, info = filter_info(info)
self.opt.reset()
gripper = get_gripper(info)
self.geom = get_geometry(info, gripper)
self._check_geom(self.geom)
self.geom.create_constraints(gripper, self.opt)
gripper.create_constraints(self.opt)
self.opt.set_min_objective(create_cube_objective(xinit, self.geom.com))
xopt = self.opt.optimize(xinit, 10_000)
w_T_r = np.zeros((4, 4))
w_T_r[:3, :3] = quat2mat(xopt[3:7])
w_T_r[:4, 3] = np.concatenate((xopt[:3], np.array([1])))
w_T_g = np.zeros((4, 4))
w_T_g[:3, :3] = self.geom.orient_mat
w_T_g[:4, 3] = np.concatenate((self.geom.pos, np.array([1])))
self.g_T_r = np.linalg.inv(w_T_g) @ w_T_r
self.opt_grasp = np.array([(xopt[7] + xopt[8]) / 0.05 * 2 - 1])
if self.opt.status != 0:
raise RuntimeError("Optimization failed to converge!")
return xopt
def set_geom(self, info: Dict):
"""Set the current controller target geometry.
Args:
info: Contact information dictionary.
"""
gripper = get_gripper(info)
self.geom = get_geometry(info, gripper)
def set_xopt(self, xopt: np.ndarray):
"""Set the current solution of the controller.
Warning:
The controller target geometry has to be set first with `set_geom`!
Args:
xopt: Optimal gripper configuration.
"""
assert self.geom is not None, "Geometry has to be set to set xopt"
w_T_r = np.zeros((4, 4))
w_T_r[:3, :3] = quat2mat(xopt[3:7])
w_T_r[:4, 3] = np.concatenate((xopt[:3], np.array([1])))
w_T_g = np.zeros((4, 4))
w_T_g[:3, :3] = self.geom.orient_mat
w_T_g[:4, 3] = np.concatenate((self.geom.pos, np.array([1])))
self.g_T_r = np.linalg.inv(w_T_g) @ w_T_r
self.opt_grasp = np.array([(xopt[7] + xopt[8]) / 0.05 * 2 - 1])
def _check_geom(self, geom: Cube):
"""Check if the object contacts are in a valid configuration for the optimization.
Contacts have to lie on opposing sides of the cube since otherwise, the contact force
constraints are violated.
Args:
geom: The cube object.
Raises:
RuntimeError: The contacts don't lie on opposing sides of the cube.
"""
if abs(geom.contact_mapping[0] - geom.contact_mapping[1]) != 1:
raise RuntimeError("Contacts don't lie on opposing sides of the cube")