|  | 
| 7 | 7 | >>> servo.angle = 30  # Turn motor to 30 degrees position. | 
| 8 | 8 | """ | 
| 9 | 9 | 
 | 
|  | 10 | +import time | 
|  | 11 | +from typing import List | 
| 10 | 12 | from typing import Union | 
| 11 | 13 | 
 | 
| 12 | 14 | from pslab.instrument.waveform_generator import PWMGenerator | 
| @@ -70,3 +72,40 @@ def _get_duty_cycle(self, angle): | 
| 70 | 72 |         angle *= self._max_angle_pulse - self._min_angle_pulse  # Scale | 
| 71 | 73 |         angle += self._min_angle_pulse  # Offset | 
| 72 | 74 |         return angle / (self._frequency**-1 * MICROSECONDS) | 
|  | 75 | + | 
|  | 76 | + | 
|  | 77 | +class RoboticArm: | 
|  | 78 | +    """Robotic arm controller for up to 4 servos.""" | 
|  | 79 | + | 
|  | 80 | +    MAX_SERVOS = 4 | 
|  | 81 | + | 
|  | 82 | +    def __init__(self, servos: List[Servo]) -> None: | 
|  | 83 | +        if len(servos) > RoboticArm.MAX_SERVOS: | 
|  | 84 | +            raise ValueError( | 
|  | 85 | +                f"At most {RoboticArm.MAX_SERVOS} servos can be used, got {len(servos)}" | 
|  | 86 | +            ) | 
|  | 87 | +        self.servos = servos | 
|  | 88 | + | 
|  | 89 | +    def run_schedule(self, timeline: List[List[int]], time_step: float = 1.0) -> None: | 
|  | 90 | +        """Run a time-based schedule to move servos. | 
|  | 91 | +
 | 
|  | 92 | +        Parameters | 
|  | 93 | +        ---------- | 
|  | 94 | +        timeline : List[List[int]] | 
|  | 95 | +            A list of timesteps,where each sublist represents one timestep, | 
|  | 96 | +            with angles corresponding to each servo. | 
|  | 97 | +
 | 
|  | 98 | +        time_step : float, optional | 
|  | 99 | +             Delay in seconds between each timestep. Default is 1.0. | 
|  | 100 | +        """ | 
|  | 101 | +        if len(timeline[0]) != len(self.servos): | 
|  | 102 | +            raise ValueError("Each timestep must specify an angle for every servo") | 
|  | 103 | + | 
|  | 104 | +        tl_len = len(timeline[0]) | 
|  | 105 | +        if not all(len(tl) == tl_len for tl in timeline): | 
|  | 106 | +            raise ValueError("All timeline entries must have the same length") | 
|  | 107 | + | 
|  | 108 | +        for tl in timeline: | 
|  | 109 | +            for i, s in enumerate(self.servos): | 
|  | 110 | +                s.angle = tl[i] | 
|  | 111 | +            time.sleep(time_step) | 
0 commit comments