# Издатель (Publisher)

{% embed url="<https://youtu.be/t17WIlZJDuo>" %}

### Введение

***Издатель*** -  это инструмент, который передает сигналы внутри системы управления.

В зависимости от того что это за сигналы, в терминах теории управления мы можем говорить о входящих или исходящих сигналах, но в ROS инструментом, который передает эти сигналы всегда является **Издатель**.

Cигналы в ROS - это сообщения, несущие структуру данных, а сами сигналы возникают в какой-то определенной точке системы - *топике*.

Так, например, контроллер робота подсчитывает обороты двигателей, чтобы сказать какое расстояние прошел робот и при помощи *Издателя* передает данные об этом в ROS. Или наша программа управления - регулятор - решает повернуть робота на определенный угол и при помощи Издателя передает это управляющее воздействие в ROS.

Сейчас мы не будем разбирать *Издателя*, делающий что-то связанное с реальным роботом. Для простоты мы смоделируем элементарный пример с которого начинаются все языки программирования. А именно напишем программу выводящую на экран “Hello ROS”.

Писать мы будем на языке Python с использованием [IDE Visual Studio Code](/new_bazovyi-kurs-po-ros/lesson1/nastroika-vscode.md), но вы можете использовать любую другую среду разработки под Python (PyCharm, Notepad+ и прочее).

### Пишем первую программу

Сформулируем задачу более определенно: Мы будем писать *Издатель*, который публикует *строку* “**Hello ROS**” в топик с именем: “*welcome\_topic*”.

Назовем нашу программу: *publisher.py* и сохраним ее в папку *ros-cource* в корневой папке.

Все эти ROS-сущности: топик, Издатель, тип данных - строка, уже описаны в библиотеке по работе с ROS для Python. Она называется ***rospy*** и по умолчанию ставится вместе с установкой ROS. Для того, чтобы просто использовать эти сущности в нашей программе, нам надо подключить эту библиотеку:

```python
#! /usr/bin/env python3
# -*- coding: utf-8 -*-

import rospy
```

Теперь давайте определим тип и структуру данных, которые мы будем передавать при помощи *Издателя*. Раз мы хотим напечатать строку “**Hello ROS**”, то тип данных с очевидностью будет строка. Для того чтобы бы мы могли использовать структуру данных ROS в Python, ее надо сначала импортировать. Давайте сделаем это:

```python
from std_msgs.msg import String
```

Как вы видите есть отличие в написании. Строка в Python называется str, строка в ROS называется String с большой буквы. Если сейчас вам это кажется непривычным, не волнуйтесь - к этому дуализму структур данных вы довольно быстро привыкните.

Теперь давайте создадим и зарегистрируем в ROS нашу *ноду*. Как вы помните *ноды* это и есть отображение в ROS наших программ. Для создания *ноды* в ***rospy*** есть стандартный метод init\_node(). Как это принято в Python, доступ к методам и объектам внутри библиотеки, происходит по их полному имени, через точку после названия библиотеки. Назовем нашу *ноду* *welcome\_node* вот так:

```python
rospy.init_node("welcome_node")
```

Теперь давайте создадим объект *Издатель*. Для простоты назовем экземпляр этого объекта *pub*

```python
pub = rospy.Publisher
```

Теперь, нам необходимо указать название топика в который издатель будет публиковать данные и название структуры этих данных. Это *welcome\_topic* и *String:*

```python
pub = rospy.Publisher("/welcome_topic", String)
```

Это основные параметры при инициализации *Издателя* и уже в таком виде он будет запускаться и работать, правда с предупреждением, которое мы рассмотрим позже.

Теперь нам надо создать объект нашей структуры данных String и заполнить ее значением “Hello robot”:

```python
s = String()
s.data = "Hello robot"
```

Тут мы создаем новый объект класса *String* и присваиваем значение “Hello robot” переменной *data* этого объекта.

Теперь давайте напишем основной цикл нашей программы.

Для программ под ROS рекомендуется такая структура основного цикла:

```python
while not rospy.is_shutdown():
```

Такой вид цикла позволит нам корректно выйти из нашей программы по нажатию **Ctrl+C**

В самом цикле, мы будем публиковать значение нашей структуры данных `s` при помощи созданного нами Издателя `pub`:

```python
pub.publish(s)
```

`publish` - это **метод класса Издатель** и он просто публикует структуру данных которую вы передаете ему в качестве аргумента, в соответствии с теми параметрами которые мы указали при инициализации, т.е. структура будет *String*, а публиковать ее надо в топик "*welcome\_topic"*

Вот и все! Мы написали первую программу для ROS! Давайте сохраним ее, запустим и посмотрим как она работает.

```python
#! /usr/bin/env python3
# -*- coding: utf-8 -*-

import rospy
from std_msgs.msg import String

rospy.init_node("welcome_node")
pub = rospy.Publisher("/welcome_topic", String)

s = String()
s.data = "Hello ROS"

while not rospy.is_shutdown():
    pub.publish(s)
```

### Запускаем программу

Запустим ***roscore*** и оставим его запущенным в окне терминала. Откроем новый терминал и напишем *python3* и полный путь до файла с нашей программой:

```python
python3 ros_course/publisher.py
```

Запустилось, но выдает предупреждение о котором мы предупреждали.

```python
ros_course/publisher.py:8: SyntaxWarning: 
The publisher should be created with an explicit keyword argument 
'queue_size'. Please see http://wiki.ros.org/rospy/Overview/Publishers%20and%20Subscribers 
for more information.  pub = rospy.Publisher("/welcome_topic", String)
```

Мы поправим его позже, а пока давайте посмотрим что изменилось в системе.

Для тестирования нашей программы мы будем использовать уже известные нам сервисные утилиты - *rostopic* и *rosnode*.

Давайте посмотрим какие ноды и топики есть сейчас в системе. Для этого в новом окне терминала вызовем:&#x20;

```python
rosnode list && echo && rostopic list

---------------------------------------
/rosout
/welcome_node

/rosout
/rosout_agg
/welcome_topic
```

Мы видим две новые записи, это записи ***/welcome\_node и*** ***/welcome\_topic***&#x20;

***/welcome\_node -*** это та самая *нода*, которую мы создали в нашей программе;

***/welcome\_topic*** - это топик, в который мы публикуем наше значение.

Т.е. мы запустили нашу программу и она инициализировала в системе свою *ноду* и зарегистрировала *топик*. А как проверить, что программа работает так как мы хотим, т.е. публикует **Hello ROS** в топик ***/welcome\_topic*** ?

Для этого снова воспользуемся утилитой *rostopic* и напишем:

```python
rostopic echo /welcome_topic

------------------------------
data: "Hello ROS"
---
data: "Hello ROS"
---
data: "Hello ROS"
---
```

Отлично ! Мы видим череду сообщений "**Hello ROS*****"***, а это значит, что все работает так как надо!

### Исправляем предупреждение

Теперь давайте быстро поправим нашу программу, чтобы при ее запуске не было предупреждений, а так же изменим частоту публикации нашего сообщения в топик.

Допишем в инициализацию *Издателя* параметр размера очереди сообщений *queue\_size* и поставим его равным 10.

```python
pub = rospy.Publisher("welcome_topic", String, queue_size=10)
```

Подробнее про этот и другие дополнительные параметры инициализации Издателя и Подписчика вы можете почитать здесь:

<http://wiki.ros.org/rospy/Overview/Publishers%20and%20Subscribers>

И вишенка на торте нашей программы - поставим частоту публикации сообщения “***Hello ROS***”

В ROS есть несколько способов обеспечить требуемую частоту публикации, и сейчас мы воспользуемся самым простым, но не самым точным из всех - методом *sleep()* библиотеки **rospy**.

Этот метод приостанавливает работу программы на то количество секунд, которое мы передаем ему в качестве аргумента. Давайте напишем ***rospy.sleep(1)*** т.е. мы хотим, чтобы сразу после публикации сообщения наша программа "засыпала" на 1 секунду.

```python
#! /usr/bin/env python3
# -*- coding: utf-8 -*-

import rospy
from std_msgs.msg import String

rospy.init_node("welcome_node")
pub = rospy.Publisher("welcome_topic", String, queue_size=10)

s = String()
s.data = "Hello ROS"

while not rospy.is_shutdown():
    pub.publish(s)
    rospy.sleep(1)
```

Теперь давайте все сохраним и снова запустим нашу программу.

![](/files/2qMkyExe2KUpxOLadwQT)

Как вы видите все обязательные параметры указаны верно и никаких предупреждений программа не выдает.

Для того, чтобы убедиться, что публикация в топик ***/welcome\_topic*** действительно идёт один раз в секунду воспользуемся новым параметром утилиты *rostopic*:

```python
rostopic hz /welcome_topic

--------------------------
average rate: 0.999
	min: 1.001s max: 1.001s std dev: 0.00000s window: 2
average rate: 0.999
	min: 1.001s max: 1.001s std dev: 0.00012s window: 3
average rate: 0.999
	min: 1.001s max: 1.001s std dev: 0.00021s window: 4
average rate: 0.999
	min: 1.001s max: 1.001s std dev: 0.00020s window: 5
average rate: 0.999
	min: 1.001s max: 1.001s std dev: 0.00019s window: 6
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://voltbro.gitbook.io/new_bazovyi-kurs-po-ros/lesson3/publisher.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
