Загрузка данных


Задание 3 — по аналогии с квадратом, но для прямоугольника (стороны разной длины) и треугольника (повороты на 120°). Делай так:

**Шаг 1. Создай файл rectangle_publisher.py** в `~/ros2_ws/src/lab_turtlesim/lab_turtlesim/`

```python
#!/usr/bin/env python3

import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Twist
import math


class RectanglePublisher(Node):

    LINEAR_SPEED = 1.0
    ANGULAR_SPEED = math.pi / 2
    SIDE_DURATIONS = [2.0, 1.0, 2.0, 1.0]
    TURN_DURATION = 1.0

    def __init__(self):
        super().__init__('rectangle_publisher')
        self.publisher_ = self.create_publisher(Twist, '/turtle1/cmd_vel', 10)
        self.timer_period = 0.1
        self.timer = self.create_timer(self.timer_period, self.timer_callback)
        self.side_index = 0
        self.state = 'forward'
        self.state_start_time = self.get_clock().now()
        self.get_logger().info('RectanglePublisher node started')

    def timer_callback(self):
        try:
            if self.publisher_.get_subscription_count() == 0:
                self.get_logger().warn('No subscribers on /turtle1/cmd_vel, is turtlesim running?')
                return

            msg = Twist()
            current_time = self.get_clock().now()
            state_elapsed_time = (current_time - self.state_start_time).nanoseconds / 1e9

            if self.state == 'forward':
                side_duration = self.SIDE_DURATIONS[self.side_index]
                msg.linear.x = self.LINEAR_SPEED
                msg.angular.z = 0.0
                if state_elapsed_time >= side_duration:
                    self.state = 'turn'
                    self.state_start_time = self.get_clock().now()
                    self.get_logger().info('Turning 90 degrees...')

            elif self.state == 'turn':
                msg.linear.x = 0.0
                msg.angular.z = self.ANGULAR_SPEED
                if state_elapsed_time >= self.TURN_DURATION:
                    self.side_index = (self.side_index + 1) % len(self.SIDE_DURATIONS)
                    self.state = 'forward'
                    self.state_start_time = self.get_clock().now()
                    self.get_logger().info('Moving forward...')

            self.publisher_.publish(msg)

        except Exception as e:
            self.get_logger().error(f'Error in timer_callback: {str(e)}')


def main(args=None):
    rclpy.init(args=args)
    rectangle_publisher = RectanglePublisher()
    try:
        rclpy.spin(rectangle_publisher)
    except KeyboardInterrupt:
        rectangle_publisher.get_logger().info('Node stopped by user')
    except Exception as e:
        rectangle_publisher.get_logger().error(f'Unexpected error: {str(e)}')
    finally:
        rectangle_publisher.destroy_node()
        rclpy.shutdown()


if __name__ == '__main__':
    main()
```

**Шаг 2. Создай файл triangle_publisher.py** в той же папке

```python
#!/usr/bin/env python3

import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Twist
import math


class TrianglePublisher(Node):

    LINEAR_SPEED = 1.0
    SIDE_DURATION = 2.0
    TURN_DURATION = 1.0
    ANGULAR_SPEED = (2 * math.pi / 3) / TURN_DURATION

    def __init__(self):
        super().__init__('triangle_publisher')
        self.publisher_ = self.create_publisher(Twist, '/turtle1/cmd_vel', 10)
        self.timer_period = 0.1
        self.timer = self.create_timer(self.timer_period, self.timer_callback)
        self.state = 'forward'
        self.state_start_time = self.get_clock().now()
        self.get_logger().info('TrianglePublisher node started')

    def timer_callback(self):
        try:
            if self.publisher_.get_subscription_count() == 0:
                self.get_logger().warn('No subscribers on /turtle1/cmd_vel, is turtlesim running?')
                return

            msg = Twist()
            current_time = self.get_clock().now()
            state_elapsed_time = (current_time - self.state_start_time).nanoseconds / 1e9

            if self.state == 'forward':
                msg.linear.x = self.LINEAR_SPEED
                msg.angular.z = 0.0
                if state_elapsed_time >= self.SIDE_DURATION:
                    self.state = 'turn'
                    self.state_start_time = self.get_clock().now()
                    self.get_logger().info('Turning 120 degrees...')

            elif self.state == 'turn':
                msg.linear.x = 0.0
                msg.angular.z = self.ANGULAR_SPEED
                if state_elapsed_time >= self.TURN_DURATION:
                    self.state = 'forward'
                    self.state_start_time = self.get_clock().now()
                    self.get_logger().info('Moving forward...')

            self.publisher_.publish(msg)

        except Exception as e:
            self.get_logger().error(f'Error in timer_callback: {str(e)}')


def main(args=None):
    rclpy.init(args=args)
    triangle_publisher = TrianglePublisher()
    try:
        rclpy.spin(triangle_publisher)
    except KeyboardInterrupt:
        triangle_publisher.get_logger().info('Node stopped by user')
    except Exception as e:
        triangle_publisher.get_logger().error(f'Unexpected error: {str(e)}')
    finally:
        triangle_publisher.destroy_node()
        rclpy.shutdown()


if __name__ == '__main__':
    main()
```

**Шаг 3. Открой setup.py** и добавь обе новые ноды в `entry_points`:

```python
entry_points={
    'console_scripts': [
        'square_publisher = lab_turtlesim.square_publisher:main',
        'rectangle_publisher = lab_turtlesim.rectangle_publisher:main',
        'triangle_publisher = lab_turtlesim.triangle_publisher:main',
    ],
},
```

**Шаг 4. Пересобери пакет**

```bash
cd ~/ros2_ws
colcon build --packages-select lab_turtlesim
source install/setup.bash
```

**Шаг 5. Запусти и сделай скриншоты**

В одном терминале:
```bash
ros2 run turtlesim turtlesim_node
```

В другом:
```bash
ros2 run lab_turtlesim rectangle_publisher
```

Сделай скриншот траектории, потом перезапусти симулятор (`Ctrl+C` и заново `ros2 run turtlesim turtlesim_node`) и запусти:
```bash
ros2 run lab_turtlesim triangle_publisher
```

Скриншот траектории треугольника — для отчёта.