Files
clover/docs/ru/snippets.md
Oleg Kalachev f77843f4a5 Move ROS Noetic (#327)
* builder: Use 64-bit Raspberry Pi OS

* travis: Use 64-bit builder

* builder: Don't try to install Melodic packages on Noetic

* clover: Use package version 3, update dependencies

* travis: Enable Noetic build

* standalone_install: Auto-select Python, ROS distro

* builder: Use variable substitution for ROS_DISTRO

* builder: Add Noetic package definitions

* builder: Use variable substitution for validation

* aruco_pose, clover: Allow compiling against OpenCV 3 and 4

* builder: Add proper Noetic repository

* builder: Don't force Tornado version

Assume rosbridge_suite depends on the right one.

* builder: Install packages for Python 3

* builder/test: Use Python3 interpreter for ROS tests

TODO (?): add tests for Python2?

* builder: Use Python 3 syntax for Python 3 tests

* builder: Install rpi_ws281x for Python3

* standalone_install: Use proper Python for pytest

* builder: Install espeak for python3

* builder: Use proper path for roscore

* builder: Install rosdep, etc. for python3

* builder: Run Clever/Clover test with Python3

* builder: Use Python3 for Clever compat layer

* builder: Enable OpenCV 4.2 repository

* builder: Force versions for ROS packages that use OpenCV

Also, hold their versions so that they don't get updated for no reason.

* aruco_pose/draw: Replace OpenCV projection code with a rewrite

* builder: Don't try to install compressed_transport twice

* clover: Fix importing urllib for Python3

* aruco_pose, clover: Expose Python scripts through CMake

* clover/selfcheck: Be more python3-compatible

This is basically commit a01d199890 from buster-python3, not sure if it aged well.

* roswww_static: Add python script installation

* clover_blocks: Use Python3 syntax for exec

* aruco_pose: Remove unused code

* Melodic => Noetic in some docs

* docs: add 0.22 migration article

* docs: remove unneeded comment

* docs: python 3 updates

* docs: python 3 update in auto_setup article

* docs: add ROS Noetic transition note

* aruco.launch: add placement, length and map arguments

* genmap.py: add -o argument for output file name

* docs: use -o argument of genmap.py

* simple_offboard: correctly check manual control timeout, separate it from kill switch check

* blocks: force led_leds index to int

* docs: update and fix 0.22 migration articles

* blocks: fix set_leds with color-typed argument

* aruco_gen: Open file in binary mode for Python3 compatibility

* clover: Use proper variable in aruco.launch

* led: change default number of leds to 72

* aruco_pose: Make sure there are no undefined symbols

Also, compile in apriltag_quad_thresh.cpp - it contains some of the functions referenced
in aruco.cpp, which would otherwise be undefined.

* aruco_pose: Make vendored library compatible with older OpenCVs

* aruco_pose, clover: Reduce the amount of OpenCV libs requested

* aruco_pose, clover: Move subscriptions to the end of init

* aruco_pose: Don't expose vendored library symbols

* aruco_pose: Simplify dynamic parameter callback setting

* builder: Build with debug symbols

* clover: Attempt to respawn dying nodelets

* Change Raspberry Pi OS to latest armhf, use packages.coex.tech as a source

* Add CRYPTOGRAPHY_DONT_BUILD_RUST=1

* Fix Node.js installation

* image: use older CMake (3.13.4-1)
Fixing https://travis-ci.org/github/CopterExpress/clover/jobs/764367665#L6984

* image: update Raspberry Pi OS to 2021-03-04

* image: bring back moving ld.so.preload out of the way while building

* Fix pthreads ld error

* Try to fix pthreads ld error

* Another attempt to fix pthreads ld error

* Yet another attempt to fix pthreads ld error

* Try to fix

* Be verbose

* Temporarily disable rc and camera_markers building

* Fix standalone-install

* Revert "Temporarily disable rc and camera_markers building"

This reverts commit e119220e91.

* Try to fix

* Try to fix

* Revert "image: use older CMake (3.13.4-1)"

This reverts commit df28da0060.

* Revert "Revert "image: use older CMake (3.13.4-1)""

This reverts commit a28c774e8f.

* Verbosity

* Debugging

* More debugging

* Display all CMake variables

* Try to fix

* Another try to fix

* Revert "Another try to fix"

This reverts commit 5a4c3a0da7.

* Another try to fix

* And another

* And yet another

* Continue...

* Cleanup

* Sources lists cleanup

* More cleanup

* Restore .git directory in clover repo

* Fix building documentation

* Fix documentation building in image

* Trigger build to update ws281x package

* Test

* Disable unneeded hack

* Disable hack

* image: add cmake-modules package

* www: add viewing clover.err file from web interface

* Remove hacks

* Show nodelet version

* docs: add packages article

* image: add image-view package for recording video from topics

* Minor fix

* CI: add Docker authentication on image build

* CI: fix Bash syntax

* CI: fix authentication in Docker

* CI: move Melodic build and editorconfig-lint to GitHub Actions (#331)

* Create main.yml

* Update main.yml

* Disable native Melodic build in Travis

* Run editorconfig-lint in Actions

* Let wget be less verbose

* Test

* Test ok

* Disable editorconfig-lint in Travis

* docs: add links to hardware sources

* CI: move image building to GitHub actions (#335)

* Start working on building image in GitHub actions

* Trigger GitHub on push to any branch

* Fix TRAVIS_TAG

* Add compress image step

* Disable image build in Travis

* Add upload image step

* Fix compress image

* Fix

* Fix

* Minor fix

* Trigger build on tag

* Show images sizes not in human format

* Upload only built image

* Make prerelease

* Upload assets on release not on tags

* readme: change build badge to GitHub Actions

* readme: add support chat badge

* CI: move documentation building to GitHub Actions (#337)

* CI: change docs target branch to actions

* CI: change docs target branch to master

* CI: use gh-pages target branch for docs

* CI: split up to several workflows

* CI: remove .travis.yml

* CI: change apt to apt-get

* CI: push documentation site to the main repo

* builder: less verbosity

* CI: add new key for apt
Fixing https://github.com/CopterExpress/clover/runs/2700356960#step:3:74

* Add Noetic building to CI

* Add test for QR recognition

* Fix

* Move QR recognition test to a separate file

* Fix QR recognition code for Python 3

* Import SetLEDs, LEDStateArray, LEDState in tests

* Add more imports to tests
(from documentation)

* Fix permissions

* Fix standalone-install for Python 2

* Fix QR recognition test

* Don’t use ROS for QR recognition test

* docs: remove non-working example

* Make v4l2 device file an argument in main_camera.launch

* Wait for v4l2 device before launching the camera driver

* Use exec in waitfile

* Transfer main camera nodelet manager to main_camera.launch

* Update cv_camera version to 0.5.1

* docs: minor fix

* Revert cv_camera to 0.5.0

* Update Raspberry Pi OS to 2021-05-07

* docs: add link to the last ROS Melodic version.

Co-authored-by: Alexey Rogachevskiy <sfalexrog@gmail.com>
2021-06-08 20:13:46 +03:00

11 KiB
Raw Blame History

Примеры кода

Python

Note

При использовании кириллических символов в кодировке UTF-8 необходимо добавить в начало программы указание кодировки:

# -*- coding: utf-8 -*-

Полет в точку и ожидание окончания полета:

import math

# ...

def navigate_wait(x=0, y=0, z=0, yaw=float('nan'), speed=0.5, frame_id='', auto_arm=False, tolerance=0.2):
    navigate(x=x, y=y, z=z, yaw=yaw, speed=speed, frame_id=frame_id, auto_arm=auto_arm)

    while not rospy.is_shutdown():
        telem = get_telemetry(frame_id='navigate_target')
        if math.sqrt(telem.x ** 2 + telem.y ** 2 + telem.z ** 2) < tolerance:
            break
        rospy.sleep(0.2)

Для того, чтобы определить расстояние до целевой точки, функция использует фрейм navigate_target.

Использование функции для полета в точку x=3, y=2, z=1 относительно карты маркеров:

navigate_wait(x=3, y=2, z=1, frame_id='aruco_map')

Эту функцию можно использовать и для взлета:

navigate_wait(z=1, frame_id='body', auto_arm=True)

Посадка и ожидание окончания посадки:

def land_wait():
    land()
    while get_telemetry().armed:
        rospy.sleep(0.2)

Использование:

land_wait()

Ожидание окончания прилета в navigate-точку:

import math

# ...

def wait_arrival(tolerance=0.2):
    while not rospy.is_shutdown():
        telem = get_telemetry(frame_id='navigate_target')
        if math.sqrt(telem.x ** 2 + telem.y ** 2 + telem.z ** 2) < tolerance:
            break
        rospy.sleep(0.2)

Функция определения расстояния между двумя точками (важно: точки должны быть в одной системе координат):

import math

# ...

def get_distance(x1, y1, z1, x2, y2, z2):
    return math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2 + (z1 - z2) ** 2)

Функция для приблизительного определения расстояния (в метрах) между двумя глобальными координатами (широта/долгота):

import math

# ...

def get_distance_global(lat1, lon1, lat2, lon2):
    return math.hypot(lat1 - lat2, lon1 - lon2) * 1.113195e5

Дизарм коптера (выключение винтов, коптер упадет):

# Объявление прокси:
from mavros_msgs.srv import CommandBool
arming = rospy.ServiceProxy('mavros/cmd/arming', CommandBool)

# ...

arming(False)  # дизарм

Трансформировать позицию (PoseStamped) из одной системы координат (фрейма) в другую, используя tf2:

import tf2_ros
import tf2_geometry_msgs
from geometry_msgs.msg import PoseStamped

tf_buffer = tf2_ros.Buffer()
tf_listener = tf2_ros.TransformListener(tf_buffer)

# ...

# Создаем объект PoseStamped (либо получаем из топика):
pose = PoseStamped()
pose.header.frame_id = 'map'  # фрейм, в котором задана позиция
pose.header.stamp = rospy.get_rostime()  # момент времени, для которого задана позиция (текущее время)
pose.pose.position.x = 1
pose.pose.position.y = 2
pose.pose.position.z = 3
pose.pose.orientation.w = 1

frame_id = 'base_link'  # целевой фрейм
transform_timeout = rospy.Duration(0.2)  # таймаут ожидания трансформации

# Преобразовываем позицию из старого фрейма в новый:
new_pose = tf_buffer.transform(pose, frame_id, transform_timeout)

Определение, перевернут ли коптер:

PI_2 = math.pi / 2
telem = get_telemetry()

flipped = abs(telem.pitch) > PI_2 or abs(telem.roll) > PI_2

Расчет общего угла коптера к горизонту:

PI_2 = math.pi / 2
telem = get_telemetry()

flipped = not -PI_2 <= telem.pitch <= PI_2 or not -PI_2 <= telem.roll <= PI_2
angle_to_horizon = math.atan(math.hypot(math.tan(telem.pitch), math.tan(telem.roll)))
if flipped:
    angle_to_horizon = math.pi - angle_to_horizon

Полет по круговой траектории:

RADIUS = 0.6  # m
SPEED = 0.3  # rad / s

start = get_telemetry()
start_stamp = rospy.get_rostime()

r = rospy.Rate(10)

while not rospy.is_shutdown():
    angle = (rospy.get_rostime() - start_stamp).to_sec() * SPEED
    x = start.x + math.sin(angle) * RADIUS
    y = start.y + math.cos(angle) * RADIUS
    set_position(x=x, y=y, z=start.z)

    r.sleep()

Повторять действие с частотой 10 Гц:

r = rospy.Rate(10)
while not rospy.is_shutdown():
    # Do anything
    r.sleep()

Пример подписки на топики из MAVROS:

from geometry_msgs.msg import PoseStamped, TwistStamped
from sensor_msgs.msg import BatteryState
from mavros_msgs.msg import RCIn

# ...

def pose_update(pose):
    # Обработка новых данных о позиции коптера
    pass

# Остальные функции-обработчики
# ...

rospy.Subscriber('/mavros/local_position/pose', PoseStamped, pose_update)
rospy.Subscriber('/mavros/local_position/velocity', TwistStamped, velocity_update)
rospy.Subscriber('/mavros/battery', BatteryState, battery_update)
rospy.Subscriber('mavros/rc/in', RCIn, rc_callback)

Информацию по топикам MAVROS см. по ссылке.

Пример отправки произвольного MAVLink-сообщения коптеру:

# ...

from mavros_msgs.msg import Mavlink
from mavros import mavlink
from pymavlink import mavutil

# ...

mavlink_pub = rospy.Publisher('mavlink/to', Mavlink, queue_size=1)

# Отправка сообщения HEARTBEAT:

msg = mavutil.mavlink.MAVLink_heartbeat_message(mavutil.mavlink.MAV_TYPE_GCS, 0, 0, 0, 0, 0)
msg.pack(mavutil.mavlink.MAVLink('', 2, 1))
ros_msg = mavlink.convert_to_rosmsg(msg)

mavlink_pub.publish(ros_msg)

Реакция на переключение режима на пульте радиоуправления (может быть использовано для запуска автономного полета, см. пример):

from mavros_msgs.msg import RCIn

# Вызывается при получении новых данных с пульта
def rc_callback(data):
    # Произвольная реакция на переключение тумблера на пульте
    if data.channels[5] < 1100:
        # ...
        pass
    elif data.channels[5] > 1900:
        # ...
        pass
    else:
        # ...
        pass

# Создаем подписчик на топик с данными с пульта
rospy.Subscriber('mavros/rc/in', RCIn, rc_callback)

rospy.spin()

Сменить режим полета на произвольный:

from mavros_msgs.srv import SetMode

# ...

set_mode = rospy.ServiceProxy('mavros/set_mode', SetMode)

# ...

set_mode(custom_mode='STABILIZED')

Флип:

import math

# ...

PI_2 = math.pi / 2

def flip():
    start = get_telemetry()  # memorize starting position

    set_rates(thrust=1)  # bump up
    rospy.sleep(0.2)

    set_rates(pitch_rate=30, thrust=0.2)  # pitch flip
    # set_rates(roll_rate=30, thrust=0.2)  # roll flip

    while True:
        telem = get_telemetry()
        flipped = abs(telem.pitch) > PI_2 or abs(telem.roll) > PI_2
        if flipped:
            break

    rospy.loginfo('finish flip')
    set_position(x=start.x, y=start.y, z=start.z, yaw=start.yaw)  # finish flip

print(navigate(z=2, speed=1, frame_id='body', auto_arm=True))  # take off
rospy.sleep(10)

rospy.loginfo('flip')
flip()

Необходимо использование специальной сборки PX4 для Клевера. Перед выполнением флипа необходимо принять все меры безопасности.

Произвести калибровку гироскопа:

from pymavlink import mavutil
from mavros_msgs.srv import CommandLong
from mavros_msgs.msg import State

# ...

send_command = rospy.ServiceProxy('/mavros/cmd/command', CommandLong)

def calibrate_gyro():
    rospy.loginfo('Calibrate gyro')
    if not send_command(command=mavutil.mavlink.MAV_CMD_PREFLIGHT_CALIBRATION, param1=1).success:
        return False

    calibrating = False
    while not rospy.is_shutdown():
        state = rospy.wait_for_message('mavros/state', State)
        if state.system_status == mavutil.mavlink.MAV_STATE_CALIBRATING or state.system_status == mavutil.mavlink.MAV_STATE_UNINIT:
            calibrating = True
        elif calibrating and state.system_status == mavutil.mavlink.MAV_STATE_STANDBY:
            rospy.loginfo('Calibrating finished')
            return True

calibrate_gyro()

Note

В процессе калибровки гироскопов дрон нельзя двигать.

Динамически включать и отключать распознавание ArUco-маркеров (например, для экономии ресурсов процессора):

import rospy
import dynamic_reconfigure.client

# ...

client = dynamic_reconfigure.client.Client('aruco_detect')

# Turn markers recognition off
client.update_configuration({'enabled': False})

rospy.sleep(5)

# Turn markers recognition on
client.update_configuration({'enabled': True})