Example #1
0
    def change_pose(self, alpha, beta, gamma):
        self.alpha = alpha
        self.beta = beta
        self.gamma = gamma

        # frame_ab is the pose of frame_b wrt frame_a
        frame_01 = frame_yrotate_xtranslate(theta=-self.beta, x=self.a)
        frame_12 = frame_yrotate_xtranslate(theta=90 - self.gamma, x=self.b)
        frame_23 = frame_yrotate_xtranslate(theta=0, x=self.c)

        frame_02 = np.matmul(frame_01, frame_12)
        frame_03 = np.matmul(frame_02, frame_23)
        new_frame = frame_zrotate_xytranslate(self.coxia_axis + self.alpha,
                                              self.new_origin.x,
                                              self.new_origin.y)

        # find points wrt to body contact point
        p0 = Vector(0, 0, 0)
        p1 = p0.get_point_wrt(frame_01)
        p2 = p0.get_point_wrt(frame_02)
        p3 = p0.get_point_wrt(frame_03)

        # find points wrt to center of gravity
        p0 = deepcopy(self.new_origin)
        p0.name += "-body-contact"
        p1 = p1.get_point_wrt(new_frame, name=self.name + "-coxia")
        p2 = p2.get_point_wrt(new_frame, name=self.name + "-femur")
        p3 = p3.get_point_wrt(new_frame, name=self.name + "-tibia")

        self.all_points = [p0, p1, p2, p3]
        self.ground_contact_point = self.compute_ground_contact()
Example #2
0
    def compute_local_p0_p1_p3(self):
        self.p0 = Vector(0, 0, 0)
        self.p1 = Vector(self.hexapod.coxia, 0, 0)

        # Find p3 aka foot tip (ground contact) with respect to the local leg frame
        rho = angle_between(self.unit_coxia_vector, self.body_to_foot_vector)
        body_to_foot_length = length(self.body_to_foot_vector)
        p3x = body_to_foot_length * np.cos(np.radians(rho))
        p3z = -body_to_foot_length * np.sin(np.radians(rho))
        self.p3 = Vector(p3x, 0, p3z)
Example #3
0
def test_shared_set_points():
    points = [
        Vector(1, 2, 3, "a"),
        Vector(1, 2, 3, "b"),
        Vector(1, 2, 3, "c"),
        Vector(1, 2, 3, "d"),
    ]

    vh = VirtualHexapod(BASE_DIMENSIONS)
    update_hexapod_points(vh, 1, points)
    for leg_point, point in zip(vh.legs[1].all_points, points):
        assert leg_point is point
Example #4
0
    def update(self, poses, assume_ground_targets=True):
        self.body_rotation_frame = None
        might_twist = find_if_might_twist(self, poses)
        old_contacts = deepcopy(self.ground_contacts)

        # Update leg poses
        for pose in poses.values():
            i = pose["id"]
            self.legs[i].change_pose(pose["coxia"], pose["femur"], pose["tibia"])

        # Find new orientation of the body (new normal)
        # distance of cog from ground and which legs are on the ground
        if assume_ground_targets:
            # We are positive that our assumed target ground contact points
            # are correct then we don't have to test all possible cases
            legs, n_axis, height = gc.compute_orientation_properties(self.legs)
        else:
            legs, n_axis, height = gc2.compute_orientation_properties(self.legs)

        if n_axis is None:
            raise Exception("❗Pose Unstable. COG not inside support polygon.")

        # Tilt and shift the hexapod based on new normal
        frame = frame_to_align_vector_a_to_b(n_axis, Vector(0, 0, 1))
        self.rotate_and_shift(frame, height)
        self._update_local_frame(frame)

        # Twist around the new normal if you have to
        self.ground_contacts = [leg.ground_contact() for leg in legs]

        if might_twist:
            twist_frame = find_twist_frame(old_contacts, self.ground_contacts)
            self.rotate_and_shift(twist_frame)

        might_print_hexapod(self, poses)
Example #5
0
    def __init__(self, hexapod, ik_parameters):
        self.hexapod = hexapod
        self.params = ik_parameters
        self.leg_x_axis = Vector(1, 0, 0)
        self.update_body_and_ground_contact_points()
        self.poses = deepcopy(HEXAPOD_POSE)

        self.legs_up_in_the_air = []
        for i in range(hexapod.LEG_COUNT):
            self.store_known_points(i)
            self.store_initial_vectors()
            self.compute_local_p0_p1_p3()
            self.compute_beta_gamma_local_p2()
            self.compute_alpha_and_twist_frame(i)
            self.points = [self.p0, self.p1, self.p2, self.p3]
            # Update point wrt world frame
            self.update_to_global_points()
            # Update hexapod's points to what we computed
            update_hexapod_points(self.hexapod, i, self.points)
            # Final p1, p2, p3, beta and gamma computed at this point
            self.poses[i]["coxia"] = self.alpha
            self.poses[i]["femur"] = self.beta
            self.poses[i]["tibia"] = self.gamma

        might_print_ik(self.poses, self.params, self.hexapod)
def find_twist_to_recompute_hexapod(a, b):
    twist = angle_between(a, b)
    z_axis = Vector(0, 0, -1)
    is_ccw = is_counter_clockwise(a, b, z_axis)
    if is_ccw:
        twist = -twist

    twist_frame = rotz(twist)
    return twist, twist_frame
Example #7
0
def find_twist_frame(old_ground_contacts, new_ground_contacts):
    # This is the frame used to twist the model about the z axis

    def _make_contact_dict(contact_list):
        return {leg_point.name: leg_point for leg_point in contact_list}

    def _twist(v1, v2):
        # https://www.euclideanspace.com/maths/algebra/vectors/angleBetween/
        theta = atan2(v2.y, v2.x) - atan2(v1.y, v1.x)
        return rotz(degrees(theta))

    # Make dictionary mapping contact point name and leg_contact_point
    old_contacts = _make_contact_dict(old_ground_contacts)
    new_contacts = _make_contact_dict(new_ground_contacts)

    # Find at least one point that's the same
    same_point_name = None
    for key in old_contacts:
        if key in new_contacts:
            same_point_name = key
            break

    # We don't know how to rotate if we don't
    # know at least one point that's on the ground
    # before and after the movement,
    # so we assume that the hexapod didn't move
    if same_point_name is None:
        return np.eye(4)

    old = old_contacts[same_point_name]
    new = new_contacts[same_point_name]

    # Get the projection of these points in the ground
    old_vector = Vector(old.x, old.y, 0)
    new_vector = Vector(new.x, new.y, 0)

    twist_frame = _twist(new_vector, old_vector)

    # ❗IMPORTANT: We are assuming that because the point
    # is on the ground before and after
    # They should be at the same point after movement
    # I can't think of a case that contradicts this as of this moment
    return twist_frame
Example #8
0
    def __init__(self, f, m, s):
        self.f = f
        self.m = m
        self.s = s

        self.cog = Vector(0, 0, 0, name="center-of-gravity")
        self.head = Vector(0, s, 0, name="head")
        self.vertices = [
            Vector(m, 0, 0, name=Hexagon.VERTEX_NAMES[0]),
            Vector(f, s, 0, name=Hexagon.VERTEX_NAMES[1]),
            Vector(-f, s, 0, name=Hexagon.VERTEX_NAMES[2]),
            Vector(-m, 0, 0, name=Hexagon.VERTEX_NAMES[3]),
            Vector(-f, -s, 0, name=Hexagon.VERTEX_NAMES[4]),
            Vector(f, -s, 0, name=Hexagon.VERTEX_NAMES[5]),
        ]

        self.all_points = self.vertices + [self.cog, self.head]
Example #9
0
    def compute_when_triangle_can_form(self):
        theta = angle_opposite_of_last_side(self.d, self.hexapod.femur,
                                            self.hexapod.tibia)
        phi = angle_between(self.coxia_to_foot_vector2d, self.leg_x_axis)

        self.beta = theta - phi  # case 1 or 2
        if self.p3.z > 0:  # case 3
            self.beta = theta + phi

        z_ = self.hexapod.femur * np.sin(np.radians(self.beta))
        x_ = self.p1.x + self.hexapod.femur * np.cos(np.radians(self.beta))

        self.p2 = Vector(x_, 0, z_)
        femur_vector = vector_from_to(self.p1, self.p2)
        tibia_vector = vector_from_to(self.p2, self.p3)
        self.gamma = 90 - angle_between(femur_vector, tibia_vector)

        if self.p2.z < self.p3.z:
            raise Exception(cant_reach_alert_msg(self.leg_name, "blocking"))
Example #10
0
 def __init__(
         self,
         a,
         b,
         c,
         alpha=0,
         beta=0,
         gamma=0,
         coxia_axis=0,
         new_origin=Vector(0, 0, 0),
         name=None,
         id_number=None,
 ):
     self.a = a
     self.b = b
     self.c = c
     self.new_origin = new_origin
     self.coxia_axis = coxia_axis
     self.id = id_number
     self.name = name
     self.change_pose(alpha, beta, gamma)
Example #11
0
def is_stable(p1, p2, p3, tol=0.001):
    """
    Determines stability of the pose.
    Determine if projection of 3D point p
    onto the plane defined by p1, p2, p3
    is within a triangle defined by p1, p2, p3.
    """
    p = Vector(0, 0, 0)
    u = vector_from_to(p1, p2)
    v = vector_from_to(p1, p3)
    n = cross(u, v)
    w = vector_from_to(p1, p)
    n2 = dot(n, n)
    beta = dot(cross(u, w), n) / n2
    gamma = dot(cross(w, v), n) / n2
    alpha = 1 - gamma - beta
    # then coordinate of the projected point (p_) of point p
    # p_ = alpha * p1 + beta * p2 + gamma * p3
    min_val = -tol
    max_val = 1 + tol
    cond1 = min_val <= alpha <= max_val
    cond2 = min_val <= beta <= max_val
    cond3 = min_val <= gamma <= max_val
    return cond1 and cond2 and cond3
Example #12
0
given_dimensions = {
    "front": 65,
    "side": 101,
    "middle": 122,
    "coxia": 83,
    "femur": 108,
    "tibia": 177,
}

# ********************************
# Correct Body Vectors
# ********************************

correct_body_points = [
    Vector(x=+116.86, y=+35.03, z=+65.62, name="right-middle"),
    Vector(x=+33.27, y=+115.41, z=+65.62, name="right-front"),
    Vector(x=-91.26, y=+78.09, z=+65.62, name="left-front"),
    Vector(x=-116.86, y=-35.03, z=+65.62, name="left-middle"),
    Vector(x=-33.27, y=-115.41, z=+65.62, name="left-back"),
    Vector(x=+91.26, y=-78.09, z=+65.62, name="right-back"),
    Vector(x=+0.00, y=+0.00, z=+65.62, name="center-of-gravity"),
    Vector(x=-29.00, y=+96.75, z=+65.62, name="head"),
]

# ********************************
# Correct Leg Vectors
# ********************************

leg0_points = [
    Vector(x=+116.86, y=+35.03, z=+65.62, name="right-middle-body-contact"),
Example #13
0
given_dimensions = {
    "front": 76,
    "side": 92,
    "middle": 123,
    "coxia": 58,
    "femur": 177,
    "tibia": 151,
}

# ********************************
# Correct Body Vectors
# ********************************

correct_body_points = [
    Vector(x=+123.00, y=+0.00, z=+0.00, name="right-middle"),
    Vector(x=+76.00, y=+92.00, z=+0.00, name="right-front"),
    Vector(x=-76.00, y=+92.00, z=+0.00, name="left-front"),
    Vector(x=-123.00, y=+0.00, z=+0.00, name="left-middle"),
    Vector(x=-76.00, y=-92.00, z=+0.00, name="left-back"),
    Vector(x=+76.00, y=-92.00, z=+0.00, name="right-back"),
    Vector(x=+0.00, y=+0.00, z=+0.00, name="center-of-gravity"),
    Vector(x=+0.00, y=+92.00, z=+0.00, name="head"),
]

# ********************************
# Correct Leg Vectors
# ********************************

leg0_points = [
    Vector(x=+123.00, y=+0.00, z=+0.00, name="right-middle-body-contact"),
Example #14
0
def inverse_kinematics_update(hexapod, ik_parameters):

    tx = ik_parameters["percent_x"] * hexapod.mid
    ty = ik_parameters["percent_y"] * hexapod.side
    tz = ik_parameters["percent_z"] * hexapod.tibia
    rotx, roty, rotz = (
        ik_parameters["rot_x"],
        ik_parameters["rot_y"],
        ik_parameters["rot_z"],
    )

    hexapod.update_stance(ik_parameters["hip_stance"],
                          ik_parameters["leg_stance"])
    hexapod.detach_body_rotate_and_translate(rotx, roty, rotz, tx, ty, tz)

    if body_contact_shoved_on_ground(hexapod):
        raise Exception(BODY_ON_GROUND_ALERT_MSG)

    x_axis = Vector(1, 0, 0)
    legs_up_in_the_air = []

    for i in range(hexapod.LEG_COUNT):
        leg_name = hexapod.legs[i].name
        body_contact = hexapod.body.vertices[i]
        foot_tip = hexapod.legs[i].foot_tip()
        body_to_foot_vector = vector_from_to(body_contact, foot_tip)

        # find the coxia vector which is the vector
        # from body contact point to joint between coxia and femur limb
        projection = project_vector_onto_plane(body_to_foot_vector,
                                               hexapod.z_axis)
        unit_coxia_vector = get_unit_vector(projection)
        coxia_vector = scalar_multiply(unit_coxia_vector, hexapod.coxia)

        # coxia point / joint is the point connecting the coxia and tibia limbs
        coxia_point = add_vectors(body_contact, coxia_vector)
        if coxia_point.z < foot_tip.z:
            raise Exception(COXIA_ON_GROUND_ALERT_MSG)

        # *******************
        # 1. Compute p0, p1 and (possible) p3 wrt leg frame
        # *******************
        p0 = Vector(0, 0, 0)
        p1 = Vector(hexapod.coxia, 0, 0)

        # Find p3 aka foot tip (ground contact) with respect to the local leg frame
        rho = angle_between(unit_coxia_vector, body_to_foot_vector)
        body_to_foot_length = length(body_to_foot_vector)
        p3x = body_to_foot_length * np.cos(np.radians(rho))
        p3z = -body_to_foot_length * np.sin(np.radians(rho))
        p3 = Vector(p3x, 0, p3z)

        # *******************
        # 2. Compute p2, beta, gamma and final p3 wrt leg frame
        # *******************

        # These values are needed to compute
        # p2 aka tibia joint (point between femur limb and tibia limb)
        coxia_to_foot_vector2d = vector_from_to(p1, p3)
        d = length(coxia_to_foot_vector2d)

        # If we can form this triangle this means
        # we can probably can reach the target ground contact point
        if is_triangle(hexapod.tibia, hexapod.femur, d):
            # .................................
            # CASE A: a triangle can be formed with
            # coxia to foot vector, hexapod's femur and tibia
            # .................................
            theta = angle_opposite_of_last_side(d, hexapod.femur,
                                                hexapod.tibia)
            phi = angle_between(coxia_to_foot_vector2d, x_axis)

            beta = theta - phi  # case 1 or 2
            if p3.z > 0:  # case 3
                beta = theta + phi

            z_ = hexapod.femur * np.sin(np.radians(beta))
            x_ = p1.x + hexapod.femur * np.cos(np.radians(beta))

            p2 = Vector(x_, 0, z_)
            femur_vector = vector_from_to(p1, p2)
            tibia_vector = vector_from_to(p2, p3)
            gamma = 90 - angle_between(femur_vector, tibia_vector)

            if p2.z < p3.z:
                raise Exception(cant_reach_alert_msg(leg_name, "blocking"))
        else:
            # .................................
            # CASE B: It's impossible to reach target ground point
            # .................................
            if d + hexapod.tibia < hexapod.femur:
                raise Exception(cant_reach_alert_msg(leg_name, "femur"))
            if d + hexapod.femur < hexapod.tibia:
                raise Exception(cant_reach_alert_msg(leg_name, "tibia"))

            # Then hexapod.femur + hexapod.tibia < d:
            legs_up_in_the_air.append(leg_name)
            LEGS_TOO_SHORT, alert_msg = legs_too_short(legs_up_in_the_air)
            if LEGS_TOO_SHORT:
                raise Exception(alert_msg)

            femur_tibia_direction = get_unit_vector(coxia_to_foot_vector2d)
            femur_vector = scalar_multiply(femur_tibia_direction,
                                           hexapod.femur)
            p2 = add_vectors(p1, femur_vector)
            tibia_vector = scalar_multiply(femur_tibia_direction,
                                           hexapod.tibia)
            p3 = add_vectors(p2, tibia_vector)

            # Find beta and gamma
            gamma = 0.0
            leg_x_axis = Vector(1, 0, 0)
            beta = angle_between(leg_x_axis, femur_vector)
            if femur_vector.z < 0:
                beta = -beta

        # Final p1, p2, p3, beta and gamma computed at this point
        not_within_range, alert_msg = beta_gamma_not_in_range(
            beta, gamma, leg_name)
        if not_within_range:
            raise Exception(alert_msg)

        # *******************
        # 3. Compute alpha and twist_frame
        # Find frame used to twist the leg frame wrt to hexapod's body contact point's x axis
        # *******************
        alpha, twist_frame = find_twist_frame(hexapod, unit_coxia_vector)
        alpha = compute_twist_wrt_to_world(alpha, hexapod.body.COXIA_AXES[i])
        alpha_limit, alert_msg = angle_above_limit(alpha, ALPHA_MAX_ANGLE,
                                                   leg_name, "(alpha/coxia)")

        if alpha_limit:
            raise Exception(alert_msg)

        # *******************
        # 4. Update hexapod points and finally update the pose
        # *******************
        points = [p0, p1, p2, p3]
        might_print_points(points, leg_name)

        # Convert points from local leg coordinate frame to world coordinate frame
        for point in points:
            point.update_point_wrt(twist_frame)
            if ASSERTION_ENABLED:
                assert hexapod.body_rotation_frame is not None, "No rotation frame!"
            point.update_point_wrt(hexapod.body_rotation_frame)
            point.move_xyz(body_contact.x, body_contact.y, body_contact.z)

        might_sanity_leg_lengths_check(hexapod, leg_name, points)
        might_sanity_beta_gamma_check(beta, gamma, leg_name, points)

        # Update hexapod's points to what we computed
        update_hexapod_points(hexapod, i, points)

        poses[i]["coxia"] = alpha
        poses[i]["femur"] = beta
        poses[i]["tibia"] = gamma

    might_print_ik(poses, ik_parameters, hexapod)
    return poses, hexapod
Example #15
0
 def _init_local_frame(self):
     self.x_axis = Vector(1, 0, 0, name="hexapod x axis")
     self.y_axis = Vector(0, 1, 0, name="hexapod y axis")
     self.z_axis = Vector(0, 0, 1, name="hexapod z axis")
Example #16
0
class VirtualHexapod:
    LEG_COUNT = 6
    __slots__ = (
        "body",
        "legs",
        "dimensions",
        "coxia",
        "femur",
        "tibia",
        "front",
        "side",
        "mid",
        "body_rotation_frame",
        "ground_contacts",
        "x_axis",
        "y_axis",
        "z_axis",
    )

    def __init__(self, dimensions):
        self._store_attributes(dimensions)
        self._init_legs()
        self._init_local_frame()

    def update(self, poses, assume_ground_targets=True):
        self.body_rotation_frame = None
        might_twist = find_if_might_twist(self, poses)
        old_contacts = deepcopy(self.ground_contacts)

        # Update leg poses
        for pose in poses.values():
            i = pose["id"]
            self.legs[i].change_pose(pose["coxia"], pose["femur"], pose["tibia"])

        # Find new orientation of the body (new normal)
        # distance of cog from ground and which legs are on the ground
        if assume_ground_targets:
            # We are positive that our assumed target ground contact points
            # are correct then we don't have to test all possible cases
            legs, n_axis, height = gc.compute_orientation_properties(self.legs)
        else:
            legs, n_axis, height = gc2.compute_orientation_properties(self.legs)

        if n_axis is None:
            raise Exception("❗Pose Unstable. COG not inside support polygon.")

        # Tilt and shift the hexapod based on new normal
        frame = frame_to_align_vector_a_to_b(n_axis, Vector(0, 0, 1))
        self.rotate_and_shift(frame, height)
        self._update_local_frame(frame)

        # Twist around the new normal if you have to
        self.ground_contacts = [leg.ground_contact() for leg in legs]

        if might_twist:
            twist_frame = find_twist_frame(old_contacts, self.ground_contacts)
            self.rotate_and_shift(twist_frame)

        might_print_hexapod(self, poses)

    def detach_body_rotate_and_translate(self, rx, ry, rz, tx, ty, tz):
        # Detach the body of the hexapod from the legs
        # then rotate and translate body as if a separate entity
        frame = frame_rotxyz(rx, ry, rz)
        self.body_rotation_frame = frame

        for point in self.body.all_points:
            point.update_point_wrt(frame)
            point.move_xyz(tx, ty, tz)

        self._update_local_frame(frame)

    def move_xyz(self, tx, ty, tz):
        for point in self.body.all_points:
            point.move_xyz(tx, ty, tz)

        for leg in self.legs:
            for point in leg.all_points:
                point.move_xyz(tx, ty, tz)

    def update_stance(self, hip_stance, leg_stance):
        pose = deepcopy(HEXAPOD_POSE)
        pose[1]["coxia"] = -hip_stance  # right_front
        pose[2]["coxia"] = hip_stance  # left_front
        pose[4]["coxia"] = -hip_stance  # left_back
        pose[5]["coxia"] = hip_stance  # right_back

        for leg in pose.values():
            leg["femur"] = leg_stance
            leg["tibia"] = -leg_stance

        self.update(pose)

    def sum_of_dimensions(self):
        f, m, s = self.front, self.mid, self.side
        a, b, c = self.coxia, self.femur, self.tibia
        return f + m + s + a + b + c

    def _store_attributes(self, dimensions):
        self.body_rotation_frame = None
        self.dimensions = dimensions
        self.coxia = dimensions["coxia"]
        self.femur = dimensions["femur"]
        self.tibia = dimensions["tibia"]
        self.front = dimensions["front"]
        self.mid = dimensions["middle"]
        self.side = dimensions["side"]
        self.body = Hexagon(self.front, self.mid, self.side)

    def _init_legs(self):
        self.legs = []
        for i in range(VirtualHexapod.LEG_COUNT):
            linkage = Linkage(
                self.coxia,
                self.femur,
                self.tibia,
                coxia_axis=Hexagon.COXIA_AXES[i],
                new_origin=self.body.vertices[i],
                name=Hexagon.VERTEX_NAMES[i],
                id_number=i,
            )
            self.legs.append(linkage)

        self.ground_contacts = [leg.ground_contact() for leg in self.legs]

    def rotate_and_shift(self, frame, height=0):
        for vertex in self.body.all_points:
            vertex.update_point_wrt(frame, height)

        for leg in self.legs:
            leg.update_leg_wrt(frame, height)

    def _init_local_frame(self):
        self.x_axis = Vector(1, 0, 0, name="hexapod x axis")
        self.y_axis = Vector(0, 1, 0, name="hexapod y axis")
        self.z_axis = Vector(0, 0, 1, name="hexapod z axis")

    def _update_local_frame(self, frame):
        # Update the x, y, z axis centered at cog of hexapod
        self.x_axis.update_point_wrt(frame)
        self.y_axis.update_point_wrt(frame)
        self.z_axis.update_point_wrt(frame)
Example #17
0
    },
    5: {
        "coxia": -5,
        "femur": 17,
        "tibia": 2,
        "name": "right-back",
        "id": 5
    },
}

# ********************************
# Correct Body Vectors
# ********************************

correct_body_points = [
    Vector(x=+97.74, y=+69.20, z=+123.97, name="right-middle"),
    Vector(x=-3.68, y=+111.78, z=+103.96, name="right-front"),
    Vector(x=-120.97, y=+28.74, z=+146.94, name="left-front"),
    Vector(x=-97.74, y=-69.20, z=+195.60, name="left-middle"),
    Vector(x=+3.68, y=-111.78, z=+215.60, name="left-back"),
    Vector(x=+120.97, y=-28.74, z=+172.63, name="right-back"),
    Vector(x=+0.00, y=+0.00, z=+159.78, name="center-of-gravity"),
    Vector(x=-62.33, y=+70.26, z=+125.45, name="head"),
]

# ********************************
# Leg Vectors
# ********************************

leg0_points = [
    Vector(x=+97.74, y=+69.20, z=+123.97, name="right-middle-body-contact"),
Example #18
0
    },
    5: {
        "name": "right-back",
        "id": 5,
        "coxia": -38.67228081907621,
        "femur": 18.790558327957655,
        "tibia": -30.220554892132796,
    },
}

# ********************************
# Correct Body Vectors
# ********************************

correct_body_points = [
    Vector(x=+112.68, y=+45.33, z=+126.66, name="right-middle"),
    Vector(x=+5.57, y=+122.87, z=+116.68, name="right-front"),
    Vector(x=-90.76, y=+84.12, z=+95.32, name="left-front"),
    Vector(x=-112.68, y=-45.33, z=+76.69, name="left-middle"),
    Vector(x=-5.57, y=-122.87, z=+86.67, name="left-back"),
    Vector(x=+90.76, y=-84.12, z=+108.03, name="right-back"),
    Vector(x=+0.00, y=+0.00, z=+101.67, name="center-of-gravity"),
    Vector(x=-42.60, y=+103.49, z=+106.00, name="head"),
]

# ********************************
# Leg Vectors
# ********************************

leg0_points = [
    Vector(x=+112.68, y=+45.33, z=+126.66, name="right-middle-body-contact"),