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

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

### Введение

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

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

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

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

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

Писать мы будем на языке Python с использованием [IDE Visual Studio Code](https://voltbro.gitbook.io/new_bazovyi-kurs-po-ros/lesson1/nastroika-vscode), но вы можете использовать любую другую среду разработки под 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)
```

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

![](https://278680980-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8k5AErXJnw5qW5Fo5HbH%2Fuploads%2FKkydCc228gRitKRjEdWb%2Ffirst-publisher.png?alt=media\&token=d37182f3-346e-4f7e-8386-c0526a897212)

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

Для того, чтобы убедиться, что публикация в топик ***/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
```
