Skip to content

Commit b186d97

Browse files
Js ipm (#920)
* wip * wip * fsdf * rggre * modified the cam library to closer to standard CV notation. * refactored the navigator path plaNNING, PID, walker to integrate the RL walk as a main mode. seperated head control from navigator. created a walk interface that can switch engine types. cleaned up navigator to be a lot more streame lined. fixed most of the unit tests. * made yaw cmds scale more inline with x,y. Seperated yaw control from x,y control. cleaned up the notation of using the PID. extended head controller logic to account when it doesnt see the object and to send a yaw cmd to the walk engine when the head motors cant center the target. * ff
1 parent 5fdbaa8 commit b186d97

35 files changed

Lines changed: 1651 additions & 1186 deletions

soccer_control/soccer_pycontrol/test/__init__.py renamed to .ai/mcp/mcp.json

File renamed without changes.

.env

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/ros/humble/lib/

soccer_control/soccer_pycontrol/config/bez2/bez2_sim_mujoco.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ walking_roll_offset: 0.0
2525
# Walk parameters - if double_support_ratio is not set to 0, should be greater than replan_frequency
2626
# Timing parameters
2727
control_frequency: 0.005
28-
single_support_duration: 0.3 # Duration of single support phase [s]
28+
single_support_duration: 0.25 # Duration of single support phase [s]
2929
single_support_timesteps: 10 # Number of planning timesteps per single support phase
3030
double_support_ratio: 0.0 # Ratio of double support (0.0 to 1.0)
3131
startend_double_support_ratio: 1.5 # Ratio duration of supports for starting and stopping walk
@@ -35,7 +35,7 @@ replan_timesteps: 10 # Replanning each n timesteps
3535
# Posture parameters
3636
walk_com_height: 0.24 # Constant height for the CoM [m]
3737
walk_foot_height: 0.05 # Height of foot rising while walking [m]
38-
walk_trunk_pitch: 0.15 # Trunk pitch angle [rad]
38+
walk_trunk_pitch: 0.2 # Trunk pitch angle [rad]
3939
walk_foot_rise_ratio: 0.2 # Time ratio for the foot swing plateau (0.0 to 1.0)
4040

4141
# Feet parameters

soccer_control/soccer_pycontrol/soccer_pycontrol/exp/stabilize_phase.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ def apply_phase_difference_roll_feedback(self, t, imu_pose: Transformation, robo
8787
F = self.walking_pid_roll.update(y_n)
8888
return min(t + F, robot_path.duration())
8989

90-
def reset_imus(self):
90+
def reset(self):
9191
"""
9292
Reset the walking and standing PID values
9393
"""

soccer_control/soccer_pycontrol/soccer_pycontrol/model/motor_control.py

Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -13,36 +13,38 @@ def wrapToPi(num: float) -> float: # TODO put in common
1313
return rem
1414

1515

16-
# class MotorData:
17-
# def __init__(self, motor_names: dict):
18-
# self.motor_names = motor_names
19-
# self.data = [0.0] * len(motor_names)
20-
#
21-
# def __getitem__(self, index):
22-
# if isinstance(index, slice):
23-
# if type(index.start) is str:
24-
# return self.data[self.motor_names[index.start][1] : self.motor_names[index.stop][1] + 1]
25-
# else:
26-
# return self.data[slice(index.start, index.stop)]
27-
# if type(index) is str:
28-
# return self.data[self.motor_names[index][1]]
29-
#
30-
# return self.data[index]
31-
#
32-
# def __setitem__(self, index, value):
33-
# if isinstance(index, slice):
34-
# if type(index.start) is str:
35-
# self.data[self.motor_names[index.start][1] : self.motor_names[index.stop][1] + 1] = value
36-
# else:
37-
# self.data[slice(index.start, index.stop)] = value
38-
# else:
39-
# if type(index) is str:
40-
# self.data[self.motor_names[index][1]] = value
41-
# else:
42-
# self.data[index] = value
43-
#
44-
# def reset(self):
45-
# self.data = [0.0] * len(self.motor_names)
16+
class MotorData:
17+
def __init__(self, motor_names: dict):
18+
self.motor_names = motor_names
19+
self.data = [0.0] * len(motor_names)
20+
21+
def __getitem__(self, index):
22+
if isinstance(index, slice):
23+
if type(index.start) is str:
24+
return self.data[self.motor_names[index.start][1] : self.motor_names[index.stop][1] + 1]
25+
else:
26+
return self.data[slice(index.start, index.stop)]
27+
if type(index) is str:
28+
return self.data[self.motor_names[index][1]]
29+
30+
return self.data[index]
31+
32+
def __setitem__(self, index, value):
33+
if isinstance(index, slice):
34+
if type(index.start) is str:
35+
self.data[self.motor_names[index.start][1] : self.motor_names[index.stop][1] + 1] = value
36+
else:
37+
self.data[slice(index.start, index.stop)] = value
38+
else:
39+
if type(index) is str:
40+
self.data[self.motor_names[index][1]] = value
41+
else:
42+
self.data[index] = value
43+
44+
def reset(self):
45+
self.data = [0.0] * len(self.motor_names)
46+
47+
4648
class MotorControl:
4749
"""
4850
Class controls access to motor information and sets motor angles in MuJoCo
@@ -63,7 +65,7 @@ def __init__(self, model: mujoco.MjModel, data: mujoco.MjData):
6365
self.ctrl_dofs_to_index = {}
6466
for name in self.dof_names:
6567
self.ctrl_dofs_to_index[name] = self.get_actuator_index(name)
66-
print() # TODO this whole thing should be a dict
68+
6769
self.configuration = np.zeros(self.dof)
6870
self.configuration_offset = np.zeros(self.dof)
6971

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
from soccer_common import PID
2+
3+
4+
class HeadControl:
5+
def __init__(self, bez):
6+
self.ball_dx = 0
7+
self.ball_dy = 0.7
8+
self.last_ball_pixel = [0, 0]
9+
self.bez = bez
10+
self.ball_x_pid = PID(
11+
Kp=0.05,
12+
Kd=0,
13+
Ki=0.03,
14+
setpoint=0,
15+
output_limits=(-3.14, 3.14),
16+
)
17+
18+
self.ball_y_pid = PID(
19+
Kp=-0.05,
20+
Kd=0,
21+
Ki=-0.03,
22+
setpoint=2.4,
23+
output_limits=(0.1, 1.3),
24+
)
25+
26+
ctrl_dt = 1/50.0
27+
self.n_substeps = int(round(ctrl_dt/self.bez.world.dt))
28+
self.counter = 0
29+
self.time_since_last_center = 0
30+
self.time_since_last_ball = 0
31+
32+
33+
def track_ball(self,ball_pixel=[0, 0]):
34+
self.counter += 1
35+
self.time_since_last_center += 1
36+
self.time_since_last_ball += 1
37+
ret_msg = -self.ball_dx
38+
if self.counter % self.n_substeps == 0:
39+
if ball_pixel != self.last_ball_pixel:
40+
self.time_since_last_ball = 0
41+
# print(f"here: {ball_pixel}, {self.ball_dx}, {self.ball_dy}")
42+
self.last_ball_pixel = ball_pixel
43+
self.ball_dx = self.ball_x_pid.update(3.2 - ball_pixel[0] / 100.0) # TODO change with center pixel from cam settign
44+
self.ball_dy = self.ball_y_pid.update(ball_pixel[1] / 100.0)
45+
if abs(ball_pixel[0] - 320.5) < 30:
46+
self.time_since_last_center = 0
47+
48+
# print(f"{ball_pixel}, {self.ball_dx}, {self.ball_dy}")
49+
self.bez.motor_control.set_head_target_angles([-self.ball_dx, self.ball_dy])
50+
# self.bez.motor_control.set_single_motor("head_pitch", self.ball_dy)
51+
self.bez.motor_control.set_motor()
52+
# print(f"here: {self.time_since_last_center * self.bez.world.dt}, {self.time_since_last_ball * self.bez.world.dt} {self.ball_dx}, {self.ball_dy}")
53+
if self.time_since_last_center * self.bez.world.dt > 1.0:
54+
ret_msg = -self.ball_dx
55+
if self.time_since_last_ball * self.bez.world.dt > 1.0:
56+
self.bez.motor_control.set_head_target_angles([0, 0.4])
57+
self.bez.motor_control.set_motor()
58+
ret_msg = 0 #-self.bez.sensors.get_pose().orientation_euler[0]
59+
return ret_msg

0 commit comments

Comments
 (0)