mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-10-25 03:13:34 +08:00
Move PX4 Guide source into /docs (#24490)
* Add vitepress tree * Update existing workflows so they dont trigger on changes in the docs path * Add nojekyll, package.json, LICENCE etc * Add crowdin docs upload/download scripts * Add docs flaw checker workflows * Used docs prefix for docs workflows * Crowdin obvious fixes * ci: docs move to self hosted runner runs on a beefy server for faster builds Signed-off-by: Ramon Roche <mrpollo@gmail.com> * ci: don't run build action for docs or ci changes Signed-off-by: Ramon Roche <mrpollo@gmail.com> * ci: update runners Signed-off-by: Ramon Roche <mrpollo@gmail.com> * Add docs/en * Add docs assets and scripts * Fix up editlinks to point to PX4 sources * Download just the translations that are supported * Add translation sources for zh, uk, ko * Update latest tranlsation and uorb graphs * update vitepress to latest --------- Signed-off-by: Ramon Roche <mrpollo@gmail.com> Co-authored-by: Ramon Roche <mrpollo@gmail.com>
This commit is contained in:
co-authored by
Ramon Roche
parent
8e6d2ebe4a
commit
88d623bedb
@@ -0,0 +1,45 @@
|
||||
# ROS 2
|
||||
|
||||
[ROS 2](https://docs.ros.org/en/humble/#) is a powerful general purpose robotics library that can be used with the PX4 Autopilot to create powerful drone applications.
|
||||
|
||||
:::warning
|
||||
Tip
|
||||
The PX4 development team highly recommend that you use/migrate to this version of ROS!
|
||||
|
||||
This is the newest version of [ROS](http://www.ros.org/) (Robot Operating System).
|
||||
It significantly improves on ROS "1", and in particular allows a much deeper and lower-latency integration with PX4.
|
||||
:::
|
||||
|
||||
ROS користується активною екосистемою розробників, які вирішують загальні проблеми робототехніки, а також має доступ до інших бібліотек програмного забезпечення, написаних для Linux.
|
||||
It can be used, for example, for [computer vision](../computer_vision/index.md) solutions.
|
||||
|
||||
ROS 2 enables a very deep integration with PX4, to the extent that you can create flight modes in ROS 2 that are indistinguisable from internal PX4 modes, and directly read from and write to internal uORB topics at high rate.
|
||||
It is recommended (in particular) for control and communication from a companion computer where low latency is important, when leveraging existing libraries from Linux, or when writing new high level flight modes.
|
||||
|
||||
Communication between ROS 2 and PX4 uses middleware that implements the [XRCE-DDS protocol](../middleware/uxrce_dds.md).
|
||||
This middleware exposes PX4 [uORB messages](../msg_docs/index.md) as ROS 2 messages and types, effectively allowing direct access to PX4 from ROS 2 workflows and nodes.
|
||||
Проміжна програма використовує визначення повідомлень uORB для генерації коду для серіалізації та десеріалізації заголовків повідомлень PX4.
|
||||
Ці ж визначення повідомлень використовуються в програмах ROS 2, щоб дозволити інтерпретувати повідомлення.
|
||||
|
||||
:::info
|
||||
ROS 2 can also connect with PX4 using [MAVROS](https://github.com/mavlink/mavros/tree/ros2/mavros) instead of XRCE-DDS.
|
||||
This option is supported by the MAVROS project (it is not documented here).
|
||||
:::
|
||||
|
||||
To use the [ROS 2](../ros2/user_guide.md) over XRCE-DDS effectively, you must (at time of writing) have a reasonable understanding of the PX4 internal architecture and conventions, which differ from those used by ROS.
|
||||
У найближчому майбутньому ми плануємо надати ROS 2 API до абстрактних конвенцій PX4, разом із прикладами, що демонструють їх використання.
|
||||
|
||||
## Topics
|
||||
|
||||
Основні теми в цьому розділі є:
|
||||
|
||||
- [ROS 2 User Guide](../ros2/user_guide.md): A PX4-centric overview of ROS 2, covering installation, setup, and how to build ROS 2 applications that communicate with PX4.
|
||||
- [ROS 2 Offboard Control Example](../ros2/offboard_control.md): A C++ tutorial examples showing how to do position control in [offboard mode](../flight_modes/offboard.md) from a ROS 2 node.
|
||||
- [ROS 2 Multi Vehicle Simulation](../ros2/multi_vehicle.md): Instructions for connecting to multipole PX4 simulations via single ROS 2 agent.
|
||||
- [PX4 ROS 2 Interface Library](../ros2/px4_ros2_interface_lib.md): A C++ library that simplies interacting with PX4 from ROS 2.
|
||||
Can be used to create and register flight modes wrtten using ROS2 and send position estimates from ROS2 applications such as a VIO system.
|
||||
- [ROS 2 Message Translation Node](../ros2/px4_ros2_msg_translation_node.md): A ROS 2 message translation node that enables communcation between PX4 and ROS 2 applications that were compiled with different sets of messages versions.
|
||||
|
||||
## Подальша інформація
|
||||
|
||||
- [XRCE-DDS (PX4-ROS 2/DDS Bridge)](../middleware/uxrce_dds.md): PX4 middleware for connecting to ROS 2.
|
||||
@@ -0,0 +1,54 @@
|
||||
# Multi-Vehicle Симуляція з ROS 2
|
||||
|
||||
[XRCE-DDS](../middleware/uxrce_dds.md) allows for multiple clients to be connected to the same agent over UDP.
|
||||
Це особливо корисно в симуляції, якщо потрібно запустити лише один агент.
|
||||
|
||||
## Налаштування та вимоги
|
||||
|
||||
Єдині вимоги
|
||||
|
||||
- To be able to run [multi-vehicle simulation](../simulation/multi-vehicle-simulation.md) without ROS 2 with the desired simulator ([Gazebo](../sim_gazebo_gz/multi_vehicle_simulation.md), [Gazebo Classic](../sim_gazebo_classic/multi_vehicle_simulation.md#multiple-vehicle-with-gazebo-classic), [FlightGear](../sim_flightgear/multi_vehicle.md) and [JMAVSim](../sim_jmavsim/multi_vehicle.md)).
|
||||
- To be able to use [ROS 2](../ros2/user_guide.md) in a single vehicle simulation.
|
||||
|
||||
## Принцип операції
|
||||
|
||||
In simulation each PX4 instance receives a unique `px4_instance` number starting from `0`.
|
||||
This value is used to assign a unique value to [UXRCE_DDS_KEY](../advanced_config/parameter_reference.md#UXRCE_DDS_KEY):
|
||||
|
||||
```sh
|
||||
param set UXRCE_DDS_KEY $((px4_instance+1))
|
||||
```
|
||||
|
||||
:::info
|
||||
By doing so, `UXRCE_DDS_KEY` will always coincide with [MAV_SYS_ID](../advanced_config/parameter_reference.md#MAV_SYS_ID).
|
||||
:::
|
||||
|
||||
Moreover, when `px4_instance` is greater than zero, a unique ROS 2 [namespace prefix](../middleware/uxrce_dds.md#customizing-the-namespace) in the form `px4_$px4_instance` is added:
|
||||
|
||||
```sh
|
||||
uxrce_dds_ns="-n px4_$px4_instance"
|
||||
```
|
||||
|
||||
:::info
|
||||
The environment variable `PX4_UXRCE_DDS_NS`, if set, will override the namespace behavior described above.
|
||||
:::
|
||||
|
||||
The first instance (`px4_instance=0`) does not have an additional namespace in order to be consistent with the default behavior of the `xrce-dds` client on a real vehicle.
|
||||
This mismatch can be fixed by manually using `PX4_UXRCE_DDS_NS` on the first instance or by starting adding vehicles from index `1` instead of `0` (this is the default behavior adopted by [sitl_multiple_run.sh](https://github.com/PX4/PX4-Autopilot/blob/main/Tools/simulation/gazebo-classic/sitl_multiple_run.sh) for Gazebo Classic).
|
||||
|
||||
Типове налаштування клієнта у симуляції:
|
||||
|
||||
| `PX4_UXRCE_DDS_NS` | `px4_instance` | `UXRCE_DDS_KEY` | client namespace |
|
||||
| ------------------ | -------------- | ---------------- | --------------------- |
|
||||
| not provided | 0 | `px4_instance+1` | нічого |
|
||||
| provided | 0 | `px4_instance+1` | `PX4_UXRCE_DDS_NS` |
|
||||
| not provided | > 0 | `px4_instance+1` | `px4_${px4_instance}` |
|
||||
| provided | > 0 | `px4_instance+1` | `PX4_UXRCE_DDS_NS` |
|
||||
|
||||
## Adjusting the `target_system` value
|
||||
|
||||
PX4 accepts [VehicleCommand](../msg_docs/VehicleCommand.md) messages only if their `target_system` field is zero (broadcast communication) or coincides with `MAV_SYS_ID`.
|
||||
У всіх інших ситуаціях повідомлення ігноруються.
|
||||
Therefore, when ROS 2 nodes want to send `VehicleCommand` to PX4, they must ensure that the messages are filled with the appropriate `target_system` value.
|
||||
|
||||
For example, if you want to send a command to your third vehicle, which has `px4_instance=2`, you need to set `target_system=3` in all your `VehicleCommand` messages.
|
||||
@@ -0,0 +1,222 @@
|
||||
# ROS 2 Offboard Control Приклад
|
||||
|
||||
The following C++ example shows how to do position control in [offboard mode](../flight_modes/offboard.md) from a ROS 2 node.
|
||||
|
||||
Приклад починає відправляти установочні точки, входить у режим offboard, озброюється, піднімається на 5 метрів і чекає.
|
||||
Незважаючи на простоту, він демонструє основні принципи використання offboard control і способи надсилання команд транспортному засобу.
|
||||
|
||||
It has been tested on Ubuntu 20.04 with ROS 2 Foxy and PX4 v1.14.
|
||||
|
||||
:::warning
|
||||
_Offboard_ control is dangerous.
|
||||
Якщо ви керуєте реальним транспортним засобом, то обов'язково майте можливість отримати назад ручне керування на випадок, якщо щось піде не так.
|
||||
:::
|
||||
|
||||
:::info
|
||||
ROS and PX4 make a number of different assumptions, in particular with respect to [frame conventions](../ros/external_position_estimation.md#reference-frames-and-ros).
|
||||
Не існує неявного перетворення між типами кадрів при публікації або підписці на теми!
|
||||
|
||||
У цьому прикладі публікуються позиції у фреймі NED, як і очікує PX4.
|
||||
To subscribe to data coming from nodes that publish in a different frame (for example the ENU, which is the standard frame of reference in ROS/ROS 2), use the helper functions in the [frame_transforms](https://github.com/PX4/px4_ros_com/blob/main/src/lib/frame_transforms.cpp) library.
|
||||
:::
|
||||
|
||||
## Випробування
|
||||
|
||||
Follow the instructions in [ROS 2 User Guide](../ros2/user_guide.md) to install PX and run the simulator, install ROS 2, and start the XRCE-DDS Agent.
|
||||
|
||||
After that we can follow a similar set of steps to those in [ROS 2 User Guide > Build ROS 2 Workspace](../ros2/user_guide.md#build-ros-2-workspace) to run the example.
|
||||
|
||||
:::tip
|
||||
Make sure that QGC is connected to PX4 before running the ROS 2 node.
|
||||
This is needed because, by default, you cannot arm a vehicle without a connection to ground station (QGC) or an established RC connection (this ensures there is always the option to regain manual control).
|
||||
:::
|
||||
|
||||
Створити та запустити приклад:
|
||||
|
||||
1. Відкрийте новий термінал.
|
||||
|
||||
2. Створіть новий каталог робочого простору colcon і перейдіть до нього за допомогою:
|
||||
|
||||
```sh
|
||||
mkdir -p ~/ws_offboard_control/src/
|
||||
cd ~/ws_offboard_control/src/
|
||||
```
|
||||
|
||||
3. Clone the [px4_msgs](https://github.com/PX4/px4_msgs) repo to the `/src` directory (this repo is needed in every ROS 2 PX4 workspace!):
|
||||
|
||||
```sh
|
||||
git clone https://github.com/PX4/px4_msgs.git
|
||||
# checkout the matching release branch if not using PX4 main.
|
||||
```
|
||||
|
||||
4. Clone the example repository [px4_ros_com](https://github.com/PX4/px4_ros_com) to the `/src` directory:
|
||||
|
||||
```sh
|
||||
git clone https://github.com/PX4/px4_ros_com.git
|
||||
```
|
||||
|
||||
5. Source the ROS 2 development environment into the current terminal and compile the workspace using `colcon`:
|
||||
|
||||
:::: tabs
|
||||
|
||||
::: tab humble
|
||||
|
||||
```sh
|
||||
cd ..
|
||||
source /opt/ros/humble/setup.bash
|
||||
colcon build
|
||||
```
|
||||
|
||||
|
||||
:::
|
||||
|
||||
::: tab foxy
|
||||
|
||||
```sh
|
||||
cd ..
|
||||
source /opt/ros/foxy/setup.bash
|
||||
colcon build
|
||||
```
|
||||
|
||||
|
||||
:::
|
||||
|
||||
::::
|
||||
|
||||
6. Source the `local_setup.bash`:
|
||||
|
||||
```sh
|
||||
source install/local_setup.bash
|
||||
```
|
||||
|
||||
7. Запустіть приклад.
|
||||
|
||||
```
|
||||
ros2 run px4_ros_com offboard_control
|
||||
```
|
||||
|
||||
Транспортний засіб повинен озброїтися, піднятися на 5 метрів і потім зачекати (вічно).
|
||||
|
||||
## Імплементація
|
||||
|
||||
The source code of the offboard control example can be found in [PX4/px4_ros_com](https://github.com/PX4/px4_ros_com) in the directory [/src/examples/offboard/offboard_control.cpp](https://github.com/PX4/px4_ros_com/blob/main/src/examples/offboard/offboard_control.cpp).
|
||||
|
||||
:::info
|
||||
PX4 publishes all the messages used in this example as ROS topics by default (see [dds_topics.yaml](https://github.com/PX4/PX4-Autopilot/blob/main/src/modules/uxrce_dds_client/dds_topics.yaml)).
|
||||
:::
|
||||
|
||||
PX4 requires that the vehicle is already receiving `OffboardControlMode` messages before it will arm in offboard mode, or before it will switch to offboard mode when flying.
|
||||
In addition, PX4 will switch out of offboard mode if the stream rate of `OffboardControlMode` messages drops below approximately 2Hz.
|
||||
Необхідна поведінка реалізується за допомогою головного циклу, що виконується у вузлі ROS 2, як показано нижче:
|
||||
|
||||
```cpp
|
||||
auto timer_callback = [this]() -> void {
|
||||
|
||||
if (offboard_setpoint_counter_ == 10) {
|
||||
// Change to Offboard mode after 10 setpoints
|
||||
this->publish_vehicle_command(VehicleCommand::VEHICLE_CMD_DO_SET_MODE, 1, 6);
|
||||
|
||||
// Arm the vehicle
|
||||
this->arm();
|
||||
}
|
||||
|
||||
// OffboardControlMode needs to be paired with TrajectorySetpoint
|
||||
publish_offboard_control_mode();
|
||||
publish_trajectory_setpoint();
|
||||
|
||||
// stop the counter after reaching 11
|
||||
if (offboard_setpoint_counter_ < 11) {
|
||||
offboard_setpoint_counter_++;
|
||||
}
|
||||
};
|
||||
timer_ = this->create_wall_timer(100ms, timer_callback);
|
||||
```
|
||||
|
||||
Цикл працює на таймері тривалістю 100 мс.
|
||||
For the first 10 cycles it calls `publish_offboard_control_mode()` and `publish_trajectory_setpoint()` to send [OffboardControlMode](../msg_docs/OffboardControlMode.md) and [TrajectorySetpoint](../msg_docs/TrajectorySetpoint.md) messages to PX4.
|
||||
The `OffboardControlMode` messages are streamed so that PX4 will allow arming once it switches to offboard mode, while the `TrajectorySetpoint` messages are ignored (until the vehicle is in offboard mode).
|
||||
|
||||
After 10 cycles `publish_vehicle_command()` is called to change to offboard mode, and `arm()` is called to arm the vehicle.
|
||||
Після того, як транспортний засіб озброюється та змінює режим, він починає відстежувати встановлені точки.
|
||||
Задані точки все ще відправляються в кожному циклі, щоб транспортний засіб не виходив з режиму поза платформою.
|
||||
|
||||
The implementations of the `publish_offboard_control_mode()` and `publish_trajectory_setpoint()` methods are shown below.
|
||||
These publish the [OffboardControlMode](../msg_docs/OffboardControlMode.md) and [TrajectorySetpoint](../msg_docs/TrajectorySetpoint.md) messages to PX4 (respectively).
|
||||
|
||||
The `OffboardControlMode` is required in order to inform PX4 of the _type_ of offboard control behing used.
|
||||
Here we're only using _position control_, so the `position` field is set to `true` and all the other fields are set to `false`.
|
||||
|
||||
```cpp
|
||||
/**
|
||||
* @brief Publish the offboard control mode.
|
||||
* For this example, only position and altitude controls are active.
|
||||
*/
|
||||
void OffboardControl::publish_offboard_control_mode()
|
||||
{
|
||||
OffboardControlMode msg{};
|
||||
msg.position = true;
|
||||
msg.velocity = false;
|
||||
msg.acceleration = false;
|
||||
msg.attitude = false;
|
||||
msg.body_rate = false;
|
||||
msg.thrust_and_torque = false;
|
||||
msg.direct_actuator = false;
|
||||
msg.timestamp = this->get_clock()->now().nanoseconds() / 1000;
|
||||
offboard_control_mode_publisher_->publish(msg);
|
||||
}
|
||||
```
|
||||
|
||||
`TrajectorySetpoint` provides the position setpoint.
|
||||
In this case, the `x`, `y`, `z` and `yaw` fields are hardcoded to certain values, but they can be updated dynamically according to an algorithm or even by a subscription callback for messages coming from another node.
|
||||
|
||||
```cpp
|
||||
/**
|
||||
* @brief Publish a trajectory setpoint
|
||||
* For this example, it sends a trajectory setpoint to make the
|
||||
* vehicle hover at 5 meters with a yaw angle of 180 degrees.
|
||||
*/
|
||||
void OffboardControl::publish_trajectory_setpoint()
|
||||
{
|
||||
TrajectorySetpoint msg{};
|
||||
msg.position = {0.0, 0.0, -5.0};
|
||||
msg.yaw = -3.14; // [-PI:PI]
|
||||
msg.timestamp = this->get_clock()->now().nanoseconds() / 1000;
|
||||
trajectory_setpoint_publisher_->publish(msg);
|
||||
}
|
||||
```
|
||||
|
||||
The `publish_vehicle_command()` sends [VehicleCommand](../msg_docs/VehicleCommand.md) messages with commands to the flight controller.
|
||||
We use it above to change the mode to offboard mode, and also in `arm()` to arm the vehicle.
|
||||
While we don't call `disarm()` in this example, it is also used in the implementation of that function.
|
||||
|
||||
```cpp
|
||||
/**
|
||||
* @brief Publish vehicle commands
|
||||
* @param command Command code (matches VehicleCommand and MAVLink MAV_CMD codes)
|
||||
* @param param1 Command parameter 1
|
||||
* @param param2 Command parameter 2
|
||||
*/
|
||||
void OffboardControl::publish_vehicle_command(uint16_t command, float param1, float param2)
|
||||
{
|
||||
VehicleCommand msg{};
|
||||
msg.param1 = param1;
|
||||
msg.param2 = param2;
|
||||
msg.command = command;
|
||||
msg.target_system = 1;
|
||||
msg.target_component = 1;
|
||||
msg.source_system = 1;
|
||||
msg.source_component = 1;
|
||||
msg.from_external = true;
|
||||
msg.timestamp = this->get_clock()->now().nanoseconds() / 1000;
|
||||
vehicle_command_publisher_->publish(msg);
|
||||
}
|
||||
```
|
||||
|
||||
:::info
|
||||
[VehicleCommand](../msg_docs/VehicleCommand.md) is one of the simplest and most powerful ways to command PX4, and by subscribing to [VehicleCommandAck](../msg_docs/VehicleCommandAck.md) you can also confirm that setting a particular command was successful.
|
||||
The param and command fields map to [MAVLink commands](https://mavlink.io/en/messages/common.html#mav_commands) and their parameter values.
|
||||
:::
|
||||
|
||||
## Дивіться також
|
||||
|
||||
- [Python ROS2 offboard examples with PX4](https://github.com/Jaeyoung-Lim/px4-offboard) (Jaeyoung-Lim/px4-offboard).
|
||||
@@ -0,0 +1,523 @@
|
||||
# Інтерфейс керування PX4 ROS 2
|
||||
|
||||
<Badge type="tip" text="PX4 v1.15" /> <Badge type="warning" text="Experimental" />
|
||||
|
||||
:::warning
|
||||
Експериментальні налаштування
|
||||
At the time of writing, parts of the PX4 ROS 2 Control Interface are experimental, and hence subject to change:
|
||||
|
||||
- Архітектура та основні інтерфейси для визначення режимів ROS 2 є значною мірою стабільними і перевіряються в ІК.
|
||||
Бібліотека надає значні переваги в режимі офборду в поточному стані.
|
||||
- Лише кілька типів значень було врегульовано (інші все ще в розробці).
|
||||
Вам можуть знадобитися внутрішні теми PX4, які можуть не залишитись серверно-сумісними з часом.
|
||||
- API не повністю задокументований.
|
||||
|
||||
:::
|
||||
|
||||
[PX4 ROS 2 Interface бібліотека](../ros2/px4_ros2_interface_lib.md) - це бібліотека C++++, яка спрощує контроль PX4 з ROS 2.
|
||||
|
||||
Розробники використовують бібліотеку для створення і динамічної реєстрації режимів, написаних за допомогою ROS 2.
|
||||
Ці режими динамічно реєструються в PX4 і здаються частиною PX4 для наземної станції або іншої зовнішньої системи.
|
||||
Вони навіть можуть замінити стандартні режими в PX4 на покращені версії ROS 2, відновлюючи оригінальну версію, якщо режим ROS2 не вдасться.
|
||||
|
||||
Бібліотека також надає класи для надсилання різних типів налаштувань, починаючи від багаторівневих навігаційних завдань на високому рівні аж до прямого контролю приводу.
|
||||
Ці класи абстрагують внутрішні встановлені точки, які використовуються PX4, і, отже, їх можна використовувати для надання послідовного інтерфейсу ROS 2 для майбутніх версій PX4 і ROS.
|
||||
|
||||
PX4 ROS 2 modes are easier to implement and maintain than PX4 internal modes, and provide more resources for developers in terms of processing power and pre-existing libraries.
|
||||
Unless the mode is safety-critical, requires strict timing or very high update rates, or your vehicle doesn't have a companion computer, you should [consider using PX4 ROS 2 modes in preference to PX4 internal modes](../concept/flight_modes.md#internal-vs-external-modes).
|
||||
|
||||
## Загальний огляд
|
||||
|
||||
Ця діаграма надає концептуального уявлення про те, як режими інтерфейсу і режими керування будуть взаємодіяти з PX4.
|
||||
|
||||

|
||||
|
||||
<!-- Source: https://docs.google.com/drawings/d/1WByCfgcytnaow7r41VhYJL8OGrw1RjFO51GoPMQBCNA/edit -->
|
||||
|
||||
Наступні розділи визначають та пояснюють терміни, що використовуються в діаграмі.
|
||||
|
||||
### Означення
|
||||
|
||||
#### Режим
|
||||
|
||||
Режим визначений за допомогою бібліотеки інтерфейсу має такі властивості:
|
||||
|
||||
- Режим - це компонент, який може передавати вказані точки автомобіля для керування рухом (такі як швидкість або прямі вимикачі).
|
||||
- Режим вибирає тип значень і відправляє його під час активності.
|
||||
Він може переключитися між різними типами множин.
|
||||
- Режим не може активувати інші режими, і він повинен бути активований користувачем (через RC/GCS), контролер польоту в безпечній ситуації _mode executor_, або деяких інших зовнішніх системах.
|
||||
- Має ім'я, яке відображається у GCS.
|
||||
- Можна налаштувати вимоги до його режиму (наприклад, що він вимагає достовірної оцінки позиції).
|
||||
- Режим може виконувати різні завдання, такі як польот до цілі, спуск лебідки, скидання вантажу та повернення назад.
|
||||
- Режим може замінити режим, визначений у PX4.
|
||||
|
||||
#### Виконавець Режимів
|
||||
|
||||
Виконавець режимів - це необов'язковий компонент для розкладання режимів.
|
||||
Наприклад, виконавець режимів для індивідуальної доставки вантажу або режиму обстеження може спочатку ініціювати злет, потім переключитися на індивідуальний режим, і коли він завершиться, ініціювати повернення на базу (RTL).
|
||||
|
||||
Зокрема, він має наступні властивості:
|
||||
|
||||
- Виконавець режимів - це необов'язковий компонент, який знаходиться на рівень вище за режим.
|
||||
Це скінченний автомат, який може активувати режими і чекати їх завершення.
|
||||
- Це може зробити лише поки він є відповідним.
|
||||
Для цього виконавець має лише один власний режим (і режим може належати лише одному виконавцю).
|
||||
Цей режим служить для активації виконавця: коли користувач вибирає режим, активується власник-виконавець, який може вибрати будь-який режим.
|
||||
Він залишається відповідальним до того моменту, поки користувач не змінить режими (через RC або з GCS), або не спрацює аварійний перехід в інший режим.
|
||||
Якщо аварійна ситуація знімається, виконавець знову активується.
|
||||
- Це дозволяє існувати декільком виконавцям одночасно.
|
||||
- Виконувачі не можуть активувати інших виконавців.
|
||||
- У бібліотеці виконавець режимів завжди реалізується в комбінації з індивідуальним режимом.
|
||||
|
||||
::: info
|
||||
|
||||
- Ці визначення гарантують, що користувач може в будь-який момент забрати контроль від індивідуального режиму або виконавця, командуючи переключення режиму через RC або GCS.
|
||||
Виконавець режимів є прозорим для користувача.
|
||||
- Виконавець режиму є прозорий для користувача.
|
||||
Він вибирається і активується опосередковано через власний режим, і тому режим має бути відповідно названий.
|
||||
|
||||
:::
|
||||
|
||||
#### Перевизначення конфігурації
|
||||
|
||||
Обидва режими і виконавці можуть визначати заміну конфігурації, дозволяючи персоналізацію певних поведінок, коли режим або виконавець активний.
|
||||
|
||||
Наразі реалізовано наступне:
|
||||
|
||||
- _Вимкнення автоматичного роз'єднання_.
|
||||
Це дозволяє здійснити посадку та знову злетіти (наприклад, для скидання вантажу).
|
||||
- _Можливість відкласти неістотні збої помилки_.
|
||||
Це дозволяє виконавцю проводити дію без переривання через некритичні заходи безпеки.
|
||||
Наприклад, ігнорування заходу безпеки через низький заряд батареї, щоб операція з лебідкою могла бути завершена.
|
||||
|
||||
### Порівняння з управлінням з віддаленої платформи
|
||||
|
||||
Вищезазначені концепції мають кілька переваг перед традиційним управлінням з віддаленої платформи:
|
||||
|
||||
- Декілька вузлів або додатків можуть співіснувати і навіть працювати одночасно.
|
||||
Але лише один вузол може керувати транспортним засобом у певний момент, і цей вузол чітко визначений.
|
||||
- Режими мають відмінну назву і можуть бути відображені / вибрані в GCS.
|
||||
- Режими інтегровані з аварійною машиною стану та перевірками настроювання.
|
||||
- Типи установок, які можуть бути відправлені, чітко визначені.
|
||||
- Режими ROS 2 можуть замінити внутрішні режими контролера польоту (такі як режим повернення).
|
||||
|
||||
## Установка та перший тест
|
||||
|
||||
Для початку роботи потрібно виконати наступні кроки:
|
||||
|
||||
1. Make sure you have a working [ROS 2 setup](../ros2/user_guide.md), with [`px4_msgs`](https://github.com/PX4/px4_msgs) in the ROS 2 workspace.
|
||||
|
||||
2. Клонуйте репозиторій в робочий простір:
|
||||
|
||||
```sh
|
||||
cd $ros_workspace/src
|
||||
git clone --recursive https://github.com/Auterion/px4-ros2-interface-lib
|
||||
```
|
||||
|
||||
:::info
|
||||
Для забезпечення сумісності, використовуйте останні _main_ гілки для PX4, _px4_msgs_ та бібліотеки.
|
||||
Дивіться також [here](https://github.com/Auterion/px4-ros2-interface-lib#compatibility-with-px4).
|
||||
|
||||
:::
|
||||
|
||||
3. Побудуйте робочий простір:
|
||||
|
||||
```sh
|
||||
cd ..
|
||||
colcon build
|
||||
source install/setup.bash
|
||||
```
|
||||
|
||||
4. У іншій оболонці запустіть PX4 SITL:
|
||||
|
||||
```sh
|
||||
cd $px4-autopilot
|
||||
make px4_sitl gazebo-classic
|
||||
```
|
||||
|
||||
(тут ми використовуємо Gazebo-Classic, але ви можете використовувати будь-яку модель або симулятор)
|
||||
|
||||
5. Запустіть агента micro XRCE в новій оболонці (після цього ви можете залишити його запущеним):
|
||||
|
||||
```sh
|
||||
MicroXRCEAgent udp4 -p 8888
|
||||
```
|
||||
|
||||
6. Запустіть QGroundControl.
|
||||
|
||||
:::info
|
||||
Використовуйте QGroundControl Daily, яка підтримує динамічне оновлення списку режимів.
|
||||
|
||||
:::
|
||||
|
||||
7. Повернутись до терміналу 2 ROS, запустити один із прикладів:
|
||||
|
||||
```sh
|
||||
ros2 run example_mode_manual_cpp example_mode_manual
|
||||
```
|
||||
|
||||
Ви повинні отримати на виході режим 'Мій ручний режим' зареєстрований:
|
||||
|
||||
```sh
|
||||
[DEBUG] [example_mode_manual]: Checking message compatibility...
|
||||
[DEBUG] [example_mode_manual]: Subscriber found, continuing
|
||||
[DEBUG] [example_mode_manual]: Publisher found, continuing
|
||||
[DEBUG] [example_mode_manual]: Registering 'My Manual Mode' (arming check: 1, mode: 1, mode executor: 0)
|
||||
[DEBUG] [example_mode_manual]: Subscriber found, continuing
|
||||
[DEBUG] [example_mode_manual]: Publisher found, continuing
|
||||
[DEBUG] [example_mode_manual]: Got RegisterExtComponentReply
|
||||
[DEBUG] [example_mode_manual]: Arming check request (id=1, only printed once)
|
||||
```
|
||||
|
||||
8. На PX4 оболонці ви можете перевірити, що PX4 зареєстрував новий режим:
|
||||
|
||||
```sh
|
||||
commander status
|
||||
```
|
||||
|
||||
Вихід має містити:
|
||||
|
||||
```plain
|
||||
INFO [commander] Disarmed
|
||||
INFO [commander] navigation mode: Position
|
||||
INFO [commander] user intended navigation mode: Position
|
||||
INFO [commander] in failsafe: no
|
||||
INFO [commander] External Mode 1: nav_state: 23, name: My Manual Mode
|
||||
```
|
||||
|
||||
9. У цій точці ви також повинні побачити режим в QGroundControl :
|
||||
|
||||
|
||||
|
||||
10. Виберіть режим, переконайтеся, що у вас є ручне джерело управління (фізичний або віртуальний джойстик), та озброєння транспорту.
|
||||
Тоді режим активується, і він має вивести наступний вивід:
|
||||
|
||||
```sh
|
||||
[DEBUG] [example_mode_manual]: Mode 'My Manual Mode' activated
|
||||
```
|
||||
|
||||
11. Тепер ви готові створити свій власний режим.
|
||||
|
||||
## Як користуватися бібліотекою
|
||||
|
||||
Наступні розділи описують конкретні функціональні можливості, які надає ця бібліотека.
|
||||
Крім того, можна підписуватися на будь-яку іншу тему PX4 або публікувати у неї.
|
||||
|
||||
### Визначення класу режиму
|
||||
|
||||
У цьому розділі розглядається приклад створення класу для власного режиму.
|
||||
|
||||
Для повного застосування перегляньте приклади в репозиторії Auterion/px4-ros2-interface-lib, такі як examples/cpp/modes/manual.
|
||||
|
||||
```cpp{1,5,7-9,24-31}
|
||||
class MyMode : public px4_ros2::ModeBase // [1]
|
||||
{
|
||||
public:
|
||||
explicit MyMode(rclcpp::Node & node)
|
||||
: ModeBase(node, Settings{"My Mode"}) // [2]
|
||||
{
|
||||
// [3]
|
||||
_manual_control_input = std::make_shared<px4_ros2::ManualControlInput>(*this);
|
||||
_rates_setpoint = std::make_shared<px4_ros2::RatesSetpointType>(*this);
|
||||
}
|
||||
|
||||
void onActivate() override
|
||||
{
|
||||
// Called whenever our mode gets selected
|
||||
}
|
||||
|
||||
void onDeactivate() override
|
||||
{
|
||||
// Called when our mode gets deactivated
|
||||
}
|
||||
|
||||
void updateSetpoint(const rclcpp::Duration & dt) override
|
||||
{
|
||||
// [4]
|
||||
const Eigen::Vector3f thrust_sp{0.F, 0.F, -_manual_control_input->throttle()};
|
||||
const Eigen::Vector3f rates_sp{
|
||||
_manual_control_input->roll() * 150.F * M_PI / 180.F,
|
||||
-_manual_control_input->pitch() * 150.F * M_PI / 180.F,
|
||||
_manual_control_input->yaw() * 100.F * M_PI / 180.F
|
||||
};
|
||||
_rates_setpoint->update(rates_sp, thrust_sp);
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<px4_ros2::ManualControlInput> _manual_control_input;
|
||||
std::shared_ptr<px4_ros2::RatesSetpointType> _rates_setpoint;
|
||||
};
|
||||
```
|
||||
|
||||
- `[1]`: Спочатку ми створюємо клас, який успадковується від [`px4_ros2::ModeBase`](https://auterion.github.io/px4-ros2-interface-lib/classpx4__ros2_1_1ModeBase.html).
|
||||
- `[2]`: У конструкторі ми передаємо назву режиму. Це також дозволяє нам налаштувати деякі інші речі, наприклад, замінити внутрішній режим польоту.
|
||||
- `[3]`: Саме тут ми створюємо всі об'єкти, через які ми хочемо використовувати пізніше.
|
||||
Це може бути RC на виведення, значення типу або телеметрія. `*this` передається як `Контекст` до кожного об'єкту, який асоціює об'єкт з режимом.
|
||||
- `[4]`: Кожен раз, коли режим активний, цей метод викликається регулярно (частота оновлення залежить від типу вказаної точки).
|
||||
Ось де ми можемо працювати і створювати нову установку.
|
||||
|
||||
Після створення екземпляру цього режиму потрібно викликати mode->doRegister(), яке фактично реєструється з контролером польоту і повертає false у разі невдачі.
|
||||
У разі використання виконавця режиму, doRegister() повинно бути викликано на виконавці режиму, а не на самому режимі.
|
||||
|
||||
### Визначення класу виконавця режиму
|
||||
|
||||
У цьому розділі розглядається приклад створення класу для власного режиму.
|
||||
|
||||
```cpp{1,4-5,9-16,20,33-57}
|
||||
class MyModeExecutor : public px4_ros2::ModeExecutorBase // [1]
|
||||
{
|
||||
public:
|
||||
MyModeExecutor(rclcpp::Node & node, px4_ros2::ModeBase & owned_mode) // [2]
|
||||
: ModeExecutorBase(node, px4_ros2::ModeExecutorBase::Settings{}, owned_mode),
|
||||
_node(node)
|
||||
{ }
|
||||
|
||||
enum class State // [3]
|
||||
{
|
||||
Reset,
|
||||
TakingOff,
|
||||
MyMode,
|
||||
RTL,
|
||||
WaitUntilDisarmed,
|
||||
};
|
||||
|
||||
void onActivate() override
|
||||
{
|
||||
runState(State::TakingOff, px4_ros2::Result::Success); // [4]
|
||||
}
|
||||
|
||||
void onDeactivate(DeactivateReason reason) override { }
|
||||
|
||||
void runState(State state, px4_ros2::Result previous_result)
|
||||
{
|
||||
if (previous_result != px4_ros2::Result::Success) {
|
||||
RCLCPP_ERROR(_node.get_logger(), "State %i: previous state failed: %s", (int)state,
|
||||
resultToString(previous_result));
|
||||
return;
|
||||
}
|
||||
|
||||
switch (state) { // [5]
|
||||
case State::Reset:
|
||||
break;
|
||||
|
||||
case State::TakingOff:
|
||||
takeoff([this](px4_ros2::Result result) {runState(State::MyMode, result);});
|
||||
break;
|
||||
|
||||
case State::MyMode: // [6]
|
||||
scheduleMode(
|
||||
ownedMode().id(), [this](px4_ros2::Result result) {
|
||||
runState(State::RTL, result);
|
||||
});
|
||||
break;
|
||||
|
||||
case State::RTL:
|
||||
rtl([this](px4_ros2::Result result) {runState(State::WaitUntilDisarmed, result);});
|
||||
break;
|
||||
|
||||
case State::WaitUntilDisarmed:
|
||||
waitUntilDisarmed([this](px4_ros2::Result result) {
|
||||
RCLCPP_INFO(_node.get_logger(), "All states complete (%s)", resultToString(result));
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
rclcpp::Node & _node;
|
||||
};
|
||||
```
|
||||
|
||||
- `[1]`: Спочатку ми створюємо клас, який наслідується від [`px4_ros2::ModeExecutorBase`](https://auterion.github.io/px4-ros2-interface-lib/classpx4__ros2_1_1ModeExecutorBase.html).
|
||||
- [2]: Конструктор приймає наш власний режим, який пов'язаний з виконавцем, і передає його в конструктор ModeExecutorBase.
|
||||
- [3]: Ми визначаємо перерахування для станів, через які ми хочемо пройти.
|
||||
- [4]: Метод onActivate викликається, коли виконавець стає активним. На цій точці ми можемо почати проходити через наші стани.
|
||||
Як ви це робите - це на ваш розсуд, у цьому прикладі використовується метод runState для виконання наступного стану.
|
||||
- [5]: При переході до стану ми викликаємо асинхронний метод від ModeExecutorBase для запуску бажаного режиму: run, takeoff, rtl і так далі.
|
||||
Ці методи передають функцію, яка викликається при завершенні; зворотний виклик надає аргумент Result, який вказує, чи вдалося виконання чи ні.
|
||||
У разі успіху зворотний виклик запускає наступний стан.
|
||||
- [6]: Ми використовуємо метод scheduleMode(), щоб запустити "власний режим" виконавця, слідуючи тому ж шаблону, що й інші обробники станів.
|
||||
|
||||
### Типи установок
|
||||
|
||||
Режим може вибрати тип(и) установки, які він хоче використовувати для керування транспортним засобом.
|
||||
Використані типи також визначають сумісність з різними типами транспортних засобів.
|
||||
|
||||
Наступні розділи надають список підтримуваних типів установок:
|
||||
|
||||
- GotoSetpointType: Плавне позиціонування та (за бажанням) керування курсом
|
||||
- DirectActuatorsSetpointType: Пряме керування моторами та установками сервоприводів польотних поверхонь
|
||||
|
||||
:::tip
|
||||
The other setpoint types are currently experimental, and can be found in: [px4_ros2/control/setpoint_types/experimental](https://github.com/Auterion/px4-ros2-interface-lib/tree/main/px4_ros2_cpp/include/px4_ros2/control/setpoint_types/experimental).
|
||||
|
||||
Ви можете додати свої власні типи установок, додавши клас, який успадковується від px4_ros2::SetpointBase, встановлює прапорці конфігурації відповідно до того, що вимагає установка, а потім публікує будь-яку тему, що містить установку
|
||||
:::
|
||||
|
||||
#### Установка "перейти до" (GotoSetpointType)
|
||||
|
||||
:::info
|
||||
Цей тип установки наразі підтримується лише для багтрикоптерів.
|
||||
:::
|
||||
|
||||
Плавне керування позицією та (за бажанням) керуванням установками курсу за допомогою типу установки px4_ros2::GotoSetpointType.
|
||||
Тип установки транслюється до плавних позиційних та курсових вирівнювачів на основі FMU, сформульованих з оптимальним за часом, максимальною швидкістю зміни прискорення, з обмеженнями швидкості та прискорення.
|
||||
|
||||
Найбільш тривіальне використання полягає в простому введенні 3D-позиції в метод оновлення:
|
||||
|
||||
```cpp
|
||||
const Eigen::Vector3f target_position_m{-10.F, 0.F, 3.F};
|
||||
_goto_setpoint->update(target_position_m);
|
||||
```
|
||||
|
||||
У цьому випадку заголовок залишиться _uncontrolled_.
|
||||
Для додаткового контрольного заголовку, вкажіть його в якості другого вхідного аргументу:
|
||||
|
||||
```cpp
|
||||
const Eigen::Vector3f target_position_m{-10.F, 0.F, 3.F};
|
||||
const float heading_rad = 3.14F;
|
||||
_goto_setpoint->update(
|
||||
target_position_m,
|
||||
heading_rad);
|
||||
```
|
||||
|
||||
Додатковою особливістю установки цільової точки є динамічний контроль за межами швидкості плавних рухів (тобто максимальні горизонтальні та вертикальні швидкості трансляції, а також швидкість руху по курсу).
|
||||
Якщо, як вище, не вказано, то плавники за замовчуванням приймуть максимальні значення за замовчуванням транспортного засобу (зазвичай встановлені на фізичні обмеження).
|
||||
Плавники можуть тільки зменшувати межі швидкості, але ніколи не збільшувати.
|
||||
|
||||
```cpp
|
||||
_goto_setpoint->update(
|
||||
target_position_m,
|
||||
heading_rad,
|
||||
max_horizontal_velocity_m_s,
|
||||
max_vertical_velocity_m_s,
|
||||
max_heading_rate_rad_s);
|
||||
```
|
||||
|
||||
Усі аргументи у методі оновлення, крім позиції, є зразками у вигляді `std::optional<float>`, що означає, що якщо хтось бажає обмежити швидкість руху по курсу, але не швидкості трансляції, це можливо за допомогою `std::nullopt`:
|
||||
|
||||
```cpp
|
||||
_goto_setpoint->update(
|
||||
target_position_m,
|
||||
heading_rad,
|
||||
std::nullopt,
|
||||
std::nullopt,
|
||||
max_heading_rate_rad_s);
|
||||
```
|
||||
|
||||
#### Безпосереднє значення параметра Control (DirectActuatorsSetpointType)
|
||||
|
||||
Клапани можна безпосередньо керувати, використовуючи тип встановлення px4_ros2::DirectActuatorsSetpointType.
|
||||
Мотори і сервоприводи можна встановити незалежно один від одного.
|
||||
Будьте уважні, що призначення є транспортним засобом та специфікацією.
|
||||
Наприклад, для управління квадрокоптером потрібно встановити перші 4 мотори відповідно до його конфігурації виводу.
|
||||
|
||||
:::info
|
||||
Якщо ви хочете керувати клапаном, який не контролює рух транспортного засобу, але, наприклад, сервопривід навантаження, подивіться нижче.
|
||||
:::
|
||||
|
||||
### Керування незалежним клапаном/сервоприводом
|
||||
|
||||
Якщо ви хочете керувати незалежним клапаном (сервоприводом), дотримуйтесь цих кроків:
|
||||
|
||||
1. Налаштуйте вивід
|
||||
2. Створіть екземпляр px4_ros2::PeripheralActuatorControls у конструкторі вашого режиму.
|
||||
3. Викличте метод set(), щоб керувати клапаном(-ами).
|
||||
Це може бути зроблено незалежно від будь-яких активних встановлень.
|
||||
|
||||
### Телеметрія
|
||||
|
||||
Ви можете отримати доступ до телеметрії PX4 безпосередньо через наступні класи:
|
||||
|
||||
- [OdometryGlobalPosition](https://auterion.github.io/px4-ros2-interface-lib/classpx4__ros2_1_1OdometryGlobalPosition.html): Global position
|
||||
- [OdometryLocalPosition](https://auterion.github.io/px4-ros2-interface-lib/classpx4__ros2_1_1OdometryLocalPosition.html): Local position, velocity, acceleration, and heading
|
||||
- [OdometryAttitude](https://auterion.github.io/px4-ros2-interface-lib/classpx4__ros2_1_1OdometryAttitude.html): Vehicle attitude
|
||||
|
||||
Наприклад, ви можете надати запит на поточну позицію автомобіля наступним чином:
|
||||
|
||||
```cpp
|
||||
std::shared_ptr<px4_ros2::OdometryLocalPosition> _vehicle_local_position;
|
||||
...
|
||||
|
||||
// Get vehicle's last local position
|
||||
_vehicle_local_position->positionNed();
|
||||
|
||||
// Check last horizontal position is valid
|
||||
_vehicle_local_position->positionXYValid();
|
||||
```
|
||||
|
||||
:::info
|
||||
Ці теми надають обгортку навколо внутрішніх тем PX4, що дозволяє бібліотеці підтримувати сумісність у випадку зміни внутрішніх тем.
|
||||
Перевірте px4_ros2/odometry для нових тем, і, звісно, ви можете використовувати будь-яку тему ROS 2, опубліковану з PX4.
|
||||
:::
|
||||
|
||||
### Аварійні вимкнення та вимоги до режимів
|
||||
|
||||
Кожен режим має набір прапорців вимог.
|
||||
Ці прапорці, як правило, автоматично встановлюються в залежності від об'єктів, які використовуються в контексті режиму.
|
||||
Наприклад, коли додається керування вручну за допомогою коду нижче, прапорець вимог для керування вручну встановлюється:
|
||||
|
||||
```cpp
|
||||
_manual_control_input = std::make_shared<px4_ros2::ManualControlInput>(*this);
|
||||
```
|
||||
|
||||
Зокрема, встановлення прапорця має наступні наслідки в PX4, якщо умова не виконується:
|
||||
|
||||
- Озброєння неможливе, коли вибрано режим
|
||||
- коли вже озброєний, режим не можна вибрати
|
||||
- коли озброєний і обраний режим, відповідний аварійний режим спрацьовує (наприклад, втрата RC для вимоги до керування вручну).
|
||||
Перевірте [сторінку безпеки](../config/safety.md), щоб налаштувати безпечну поведінку.
|
||||
Аварійний режим також спрацьовує, коли режим аварійно завершується або стає несприйнятливим, коли він обраний.
|
||||
|
||||
Ось відповідна блок-схема для прапорця керування вручну:
|
||||
|
||||
|
||||
|
||||
<!-- source: https://drive.google.com/file/d/1g_NlQlw7ROLP_mAi9YY2nDwP0zTNsFlB/view -->
|
||||
|
||||
Можна вручну оновити будь-які вимоги до режиму після реєстрації.
|
||||
|
||||
```cpp
|
||||
modeRequirements().home_position = true;
|
||||
```
|
||||
|
||||
Повний список флагів може бути знайдений у [requirement_flags.hpp](https://github.com/Auterion/px4-ros2-interface-lib/blob/main/px4_ros2_cpp/include/px4_ros2/common/requirement_flags.hpp).
|
||||
|
||||
#### Відкладення аварійних режимів
|
||||
|
||||
Режим або режим виконавця може тимчасово відкласти неістотні збої, викликаючи метод [`deferFailsafesSync()`](https://auterion.github.io/px4-2-interface-lib/classpx4__ros2_1_1ModeExecutorBase.html#a16ec5be6e70e1d0625bf696c3e29ae).
|
||||
|
||||
Переглянте [integration test](https://github.com/Auterion/px4-ros2-interface-lib/blob/main/px4_ros2_cpp/test/integration/overrides.cpp) for an example.
|
||||
|
||||
### Призначення режиму для RC-перемикача або дії джойстика
|
||||
|
||||
Зовнішні режими можуть бути призначені на [RC перемикачі](../config/flight_mode.md) або дії джойстика.
|
||||
При призначенні режиму для RC-перемикача вам потрібно знати індекс (тому що параметри метаданих не містять динамічне ім'я режиму).
|
||||
Використовуйте статус `commander `, коли режим запущено, щоб отримати цю інформацію.
|
||||
|
||||
Наприклад:
|
||||
|
||||
```plain
|
||||
INFO [commander] External Mode 1: nav_state: 23, name: My Manual Mode
|
||||
```
|
||||
|
||||
означає, що ви б обрали **Зовнішній режим 1** в QGC:
|
||||
|
||||

|
||||
|
||||
:::info
|
||||
PX4 забезпечує, що певний режим завжди призначається тому ж індексу, зберігаючи хеш назви режиму.
|
||||
This makes it independent of startup ordering in case of multiple external modes.
|
||||
:::
|
||||
|
||||
### Заміна внутрішнього режиму
|
||||
|
||||
Зовнішній режим може замінити існуючий внутрішній режим, наприклад, режим Повернення (RTL).
|
||||
При цьому кожного разу, коли вибирається RTL (через користувача або ситуацію аварійного виклику), замість внутрішнього режиму використовується зовнішній. Внутрішній режим використовується лише як резервний випадок.
|
||||
Внутрішній режим використовується лише як резервний випадок, коли зовнішній стає недоступним або відмовляє.
|
||||
|
||||
Замінений режим можна встановити в налаштуваннях конструктора ModeBase:
|
||||
|
||||
```cpp
|
||||
Settings{kName, false, ModeBase::kModeIDRtl}
|
||||
```
|
||||
@@ -0,0 +1,73 @@
|
||||
# Інтерфейсна бібліотека PX4 ROS 2
|
||||
|
||||
<Badge type="tip" text="PX4 v1.15" /> <Badge type="warning" text="Experimental" />
|
||||
|
||||
:::warning
|
||||
Експериментальні налаштування
|
||||
На момент написання цієї статті, деякі частини бібліотеки інтерфейсу PX4 ROS 2 є експериментальними і, отже, можуть бути змінені.
|
||||
:::
|
||||
|
||||
[PX4 ROS 2 Інтерфейс бібліотеки](https://github.com/Auterion/px4-ros2-interface-lib) є бібліотекою C+++, яка спрощує контроль і взаємодіє з PX4 з ROS 2.
|
||||
|
||||
Бібліотека надає два інтерфейси високого рівня для розробників:
|
||||
|
||||
1. [Control Interface](./px4_ros2_interface.md) дозволяє розробникам створювати та динамічно реєструвати режими, написані з використанням ROS 2.
|
||||
Бібліотека також надає класи для надсилання різних типів налаштувань, починаючи від багаторівневих навігаційних завдань на високому рівні аж до прямого контролю приводу.
|
||||
2. [Навігаційний інтерфейс](./px4_ros2_navigation_interface.md) дозволяє надсилати позицію автомобіля з позиції PX4 з ROS 2 додатків, таких як система VIO.
|
||||
|
||||
<!--
|
||||
## Overview
|
||||
-->
|
||||
|
||||
## Встановлення в робочому просторі ROS 2
|
||||
|
||||
Щоб почати використовувати бібліотеку в існуючому робочому просторі ROS 2:
|
||||
|
||||
1. Make sure you have a working [ROS 2 setup](../ros2/user_guide.md), with [`px4_msgs`](https://github.com/PX4/px4_msgs) in the ROS 2 workspace.
|
||||
|
||||
2. Клонуйте репозиторій в робочий простір:
|
||||
|
||||
```sh
|
||||
cd $ros_workspace/src
|
||||
git clone --recursive https://github.com/Auterion/px4-ros2-interface-lib
|
||||
```
|
||||
|
||||
:::info
|
||||
Для забезпечення сумісності, використовуйте останні _main_ гілки для PX4, _px4_msgs_ та бібліотеки.
|
||||
Дивіться також [here](https://github.com/Auterion/px4-ros2-interface-lib#compatibility-with-px4).
|
||||
|
||||
:::
|
||||
|
||||
3. Побудуйте робочий простір:
|
||||
|
||||
```sh
|
||||
cd ..
|
||||
colcon build
|
||||
source install/setup.bash
|
||||
```
|
||||
|
||||
<!--
|
||||
## How to Use the Library
|
||||
-->
|
||||
|
||||
##
|
||||
|
||||
При відкритті запиту на PX4, CI запускає тест з інтеграції до бібліотеки.
|
||||
|
||||
Тести можуть також бути виконані локально з PX4:
|
||||
|
||||
```sh
|
||||
./test/ros_test_runner.py
|
||||
```
|
||||
|
||||
І щоб керувати лише одним випадком:
|
||||
|
||||
```sh
|
||||
./test/ros_test_runner.py --verbose --case <case>
|
||||
```
|
||||
|
||||
Ви можете скласти список доступних тестових кейсів з:
|
||||
|
||||
```sh
|
||||
./test/ros_test_runner.py --list-cases
|
||||
```
|
||||
@@ -0,0 +1,452 @@
|
||||
# PX4 ROS 2 Message Translation Node
|
||||
|
||||
<Badge type="tip" text="main (planned for: PX4 v1.16+)" /> <Badge type="warning" text="Experimental" />
|
||||
|
||||
The message translation node allows ROS 2 applications that were compiled against different versions of the PX4 messages to interwork with newer versions of PX4, and vice versa, without having to change either the application or the PX4 side.
|
||||
|
||||
## Загальний огляд
|
||||
|
||||
The translation of messages from one definition version to another is possible thanks to the introduction of [message versioning](../middleware/uorb.md#message-versioning).
|
||||
|
||||
The translation node has access to all message versions previously defined by PX4.
|
||||
It dynamically observes the DDS data space, monitoring the publications, subscriptions and services originating from either PX4 via the [uXRCE-DDS Bridge](../middleware/uxrce_dds.md), or ROS 2 applications.
|
||||
When necessary, it converts messages to the current versions expected by both applications and PX4, ensuring compatibility.
|
||||
|
||||

|
||||
|
||||
<!-- doc source: ../../assets/middleware/ros2/px4_ros2_interface_lib/translation_node.drawio -->
|
||||
|
||||
To support the coexistence of different versions of the same messages within the ROS 2 domain, the ROS 2 topic-names for publications, subscriptions, and services include their respective message version as a suffix.
|
||||
This naming convention takes the form `<topic_name>_v<version>`, as shown in the diagram above.
|
||||
|
||||
## Використання
|
||||
|
||||
### Встановлення
|
||||
|
||||
The following steps describe how to install and run the translation node on your machine.
|
||||
|
||||
1. (Optional) Create a new ROS 2 workspace in which to build the message translation node and its dependencies:
|
||||
|
||||
```sh
|
||||
mkdir -p /path/to/ros_ws/src
|
||||
```
|
||||
|
||||
2. Run the following helper script to copy the message definitions and translation node into your ROS workspace directory.
|
||||
|
||||
```sh
|
||||
cd /path/to/ros_ws
|
||||
/path/to/PX4-Autopilot/Tools/copy_to_ros_ws.sh .
|
||||
```
|
||||
|
||||
3. Build and source the workspace.
|
||||
|
||||
```sh
|
||||
colcon build
|
||||
source /path/to/ros_ws/install/setup.bash
|
||||
```
|
||||
|
||||
4. Finally, run the translation node.
|
||||
|
||||
```sh
|
||||
ros2 run translation_node translation_node_bin
|
||||
```
|
||||
|
||||
You should see an output similar to:
|
||||
|
||||
```sh
|
||||
[INFO] [1734525720.729530513] [translation_node]: Registered pub/sub topics and versions:
|
||||
[INFO] [1734525720.729594413] [translation_node]: Registered services and versions:
|
||||
```
|
||||
|
||||
With the translation node running, any simultaneously running ROS 2 application designed to communicate with PX4 can do so, as long as it uses message versions recognized by the node.
|
||||
The translation node will print a warning if it encounters an unknown topic version.
|
||||
|
||||
:::info
|
||||
After making a modification in PX4 to the message definitions and/or translation node code, you will need to rerun the steps above from point 2 to update your ROS workspace accordingly.
|
||||
:::
|
||||
|
||||
### In ROS Applications
|
||||
|
||||
While developing a ROS 2 application that communicates with PX4, it is not necessary to know the specific version of a message being used.
|
||||
The message version can be added generically to a topic name like this:
|
||||
|
||||
:::: tabs
|
||||
|
||||
:::tab C++
|
||||
|
||||
```c++
|
||||
topic_name + "_v" + std::to_string(T::MESSAGE_VERSION)
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::tab Python
|
||||
|
||||
```python
|
||||
topic_name + "_v" + VehicleAttitude.MESSAGE_VERSION
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
::::
|
||||
|
||||
where `T` is the message type, e.g. `px4_msgs::msg::VehicleAttitude`.
|
||||
|
||||
For example, the following implements a minimal subscriber and publisher node that uses two versioned PX4 messages and topics:
|
||||
|
||||
:::: tabs
|
||||
|
||||
:::tab C++
|
||||
|
||||
```c++
|
||||
#include <string>
|
||||
#include <rclcpp/rclcpp.hpp>
|
||||
#include <px4_msgs/msg/vehicle_command.hpp>
|
||||
#include <px4_msgs/msg/vehicle_attitude.hpp>
|
||||
|
||||
// Template function to get the message version suffix
|
||||
// The correct message version is directly inferred from the message defintion
|
||||
template <typename T>
|
||||
std::string getMessageNameVersion() {
|
||||
if (T::MESSAGE_VERSION == 0) return "";
|
||||
return "_v" + std::to_string(T::MESSAGE_VERSION);
|
||||
}
|
||||
|
||||
class MinimalPubSub : public rclcpp::Node {
|
||||
public:
|
||||
MinimalPubSub() : Node("minimal_pub_sub") {
|
||||
// Use template function to define the correct topics automatically
|
||||
const std::string sub_topic = "/fmu/out/vehicle_attitude" + getMessageNameVersion<px4_msgs::msg::VehicleAttitude>();
|
||||
const std::string pub_topic = "/fmu/in/vehicle_command" + getMessageNameVersion<px4_msgs::msg::VehicleCommand>();
|
||||
|
||||
_subscription = this->create_subscription<px4_msgs::msg::VehicleAttitude>(
|
||||
sub_topic, 10,
|
||||
std::bind(&MinimalPubSub::attitude_callback, this, std::placeholders::_1));
|
||||
_publisher = this->create_publisher<px4_msgs::msg::VehicleCommand>(pub_topic, 10);
|
||||
}
|
||||
|
||||
private:
|
||||
void attitude_callback(const px4_msgs::msg::VehicleAttitude::SharedPtr msg) {
|
||||
RCLCPP_INFO(this->get_logger(), "Received attitude message.");
|
||||
}
|
||||
|
||||
rclcpp::Publisher<px4_msgs::msg::VehicleCommand>::SharedPtr _publisher;
|
||||
rclcpp::Subscription<px4_msgs::msg::VehicleAttitude>::SharedPtr _subscription;
|
||||
};
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::tab Python
|
||||
|
||||
```python
|
||||
import rclpy
|
||||
from rclpy.node import Node
|
||||
from px4_msgs.msg import VehicleCommand, VehicleAttitude
|
||||
|
||||
# Helper function to get the message version suffix
|
||||
# The correct message version is directly inferred from the message definition
|
||||
def get_message_name_version(msg_class):
|
||||
if msg_class.MESSAGE_VERSION == 0:
|
||||
return ""
|
||||
return f"_v{msg_class.MESSAGE_VERSION}"
|
||||
|
||||
class MinimalPubSub(Node):
|
||||
def __init__(self):
|
||||
super().__init__('minimal_pub_sub')
|
||||
|
||||
# Use helper function to define the correct topics automatically
|
||||
sub_topic = f"/fmu/out/vehicle_attitude{get_message_name_version(VehicleAttitude)}"
|
||||
pub_topic = f"/fmu/in/vehicle_command{get_message_name_version(VehicleCommand)}"
|
||||
|
||||
self._subscription = self.create_subscription(
|
||||
VehicleAttitude,
|
||||
sub_topic,
|
||||
self.attitude_callback,
|
||||
10
|
||||
)
|
||||
|
||||
self._publisher = self.create_publisher(
|
||||
VehicleCommand,
|
||||
pub_topic,
|
||||
10
|
||||
)
|
||||
|
||||
def attitude_callback(self, msg):
|
||||
self.get_logger().info("Received attitude message.")
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
::::
|
||||
|
||||
On the PX4 side, the DDS client automatically adds the version suffix if a message definition contains the field `uint32 MESSAGE_VERSION = x`.
|
||||
|
||||
:::info
|
||||
Version 0 of a topic means that no `_v<version>` suffix should be added.
|
||||
:::
|
||||
|
||||
## Development
|
||||
|
||||
### Означення
|
||||
|
||||
A **message** defines the data format used for communication, whether over a topic or a service.
|
||||
Therefore a message can be either a _topic_ message defined by a `.msg` file, or a _service_ message defined by a `.srv` file.
|
||||
|
||||
A **versioned message** is a message for which changes are tracked and each change results in a version bump, with the previous state of the definition being stored in history.
|
||||
The latest version of every message is stored in `msg/versioned/` for topics (or `srv/versioned` for services), and all older versions are stored in `msg/px4_msgs_old/msg/` (or `msg/px4_msgs_old/srv/`).
|
||||
|
||||
A **version translation** defines a bidirectional mapping of the contents of one or more message definition across different versions.
|
||||
Each translation is stored as a separate `.h` header file under `msg/translation_node/translations/`.
|
||||
Message translations can be either _direct_ or _generic_.
|
||||
|
||||
- A **direct translation** defines a bidirectional mapping of the contents of a _single_ message between two of its versions.
|
||||
This is the simpler case and should be preferred if possible.
|
||||
- A **generic translation** defines a bidirectional mapping of the contents of `n` input messages to `m` output messages across different versions.
|
||||
This can be used for merging or splitting a message, or when moving a field from one message to another.
|
||||
|
||||
### File Structure
|
||||
|
||||
Starting from PX4 v1.16 (main), the PX4-Autopilot `msg/` and `srv/` directories are structured as follows:
|
||||
|
||||
```
|
||||
PX4-Autopilot
|
||||
├── ...
|
||||
├── msg/
|
||||
├── *.msg # Non-versioned topic message files
|
||||
├── versioned/ # Latest versioned topic message files
|
||||
├── px4_msgs_old/ # History of versioned messages (.msg + .srv) [ROS 2 package]
|
||||
└── translation_node/ # Translation node and translation headers [ROS 2 package]
|
||||
└── srv/
|
||||
├── *.srv # Non-versioned service message files
|
||||
└── versioned/ # Latest versioned service message files
|
||||
```
|
||||
|
||||
This structure introduces new directories: `versioned/`, `px4_msgs_old/`, and `translation_node/`.
|
||||
|
||||
#### Directories `msg/versioned/` and `srv/versioned/`
|
||||
|
||||
- Contain the current latest version of each message.
|
||||
- Files in these directories must include a `MESSAGE_VERSION` field to indicate that they are versioned.
|
||||
- File names follow the conventional naming scheme (without a version suffix).
|
||||
|
||||
Example directory structure:
|
||||
|
||||
```
|
||||
PX4-Autopilot
|
||||
├── ...
|
||||
├── msg/
|
||||
└── versioned/
|
||||
├── VehicleAttitude.msg # e.g. MESSAGE_VERSION = 3
|
||||
└── VehicleGlobalPosition.msg # e.g. MESSAGE_VERSION = 2
|
||||
└── srv/
|
||||
└── versioned/
|
||||
└── VehicleCommand.srv # e.g. MESSAGE_VERSION = 2
|
||||
```
|
||||
|
||||
#### Directory `px4_msgs_old/`
|
||||
|
||||
- Archives the history of all versioned messages, including both topic and service messages (resp. under `msg/` and `srv/` subdirectories).
|
||||
- Each file includes a `MESSAGE_VERSION` field.
|
||||
- File names reflect the message's version with a suffix (e.g., `V1`, `V2`).
|
||||
|
||||
Example directory structure (matching the example above):
|
||||
|
||||
```
|
||||
...
|
||||
msg/
|
||||
└── px4_msgs_old/
|
||||
├── msg/
|
||||
├── VehicleAttitudeV1.msg
|
||||
├── VehicleAttitudeV2.msg
|
||||
└── VehicleGlobalPositionV1.msg
|
||||
└── srv/
|
||||
└── VehicleCommandV1.srv
|
||||
```
|
||||
|
||||
#### Directory `translation_node/`
|
||||
|
||||
- Contains headers for translating between all different versions of messages.
|
||||
- Each translation (direct or generic) is a single `.h` header file.
|
||||
- The header `all_translation.h` acts as the main header, and includes all subsequent translation headers.
|
||||
|
||||
Example directory structure (matching the example above):
|
||||
|
||||
```
|
||||
...
|
||||
msg/
|
||||
└── translation_node/
|
||||
└── translations/
|
||||
├── all_translations.h # Main header
|
||||
├── translation_vehicle_attitude_v1.h # Direct translation v0 <-> v1
|
||||
├── translation_vehicle_attitude_v2.h # Direct translation v1 <-> v2
|
||||
├── translation_vehicle_attitude_v3.h # Direct translation v2 <-> latest (v3)
|
||||
├── translation_vehicle_global_position_v1.h # Direct translation v0 <-> v1
|
||||
├── translation_vehicle_global_position_v2.h # Direct translation v1 <-> latest (v2)
|
||||
├── translation_vehicle_command_v1.h # Direct translation v0 <-> v1
|
||||
└── translation_vehicle_command_v2.h # Direct translation v1 <-> latest (v2)
|
||||
```
|
||||
|
||||
### Updating a Versioned Message
|
||||
|
||||
This section provides a step-by-step walkthrough and a basic working example of what the process of changing a versioned message looks like.
|
||||
|
||||
The example describes the process of updating the `VehicleAttitude` message definition to contain an additional `new_field` entry, incrementing the message version from `3` to `4`, and creating a new direct translation in the process.
|
||||
|
||||
1. **Create an Archived Definition of the Current Versioned Message**
|
||||
|
||||
Copy the versioned `.msg` topic message file (or `.srv` service message file) to `px4_msgs_old/msg/` (or `px4_msgs_old/srv/`), and append its message version to the file name.
|
||||
|
||||
For example:<br>
|
||||
Copy `msg/versioned/VehicleAttitude.msg` → `msg/versioned/px4_msgs_old/msg/VehicleAttitudeV3.msg`
|
||||
|
||||
2. **Update Translation References to the Archived Definition**
|
||||
|
||||
Update the existing translations header files `msg/translation_node/translations/*.h` to reference the newly archived message definition.
|
||||
|
||||
For example, update references in those files:<br>
|
||||
|
||||
- Replace `px4_msgs::msg::VehicleAttitude` → `px4_msgs_old::msg::VehicleAttitudeV3`
|
||||
- Replace `#include <px4_msgs/msg/vehicle_attitude.hpp>` → `#include <px4_msgs_old/msg/vehicle_attitude_v3.hpp>`
|
||||
|
||||
3. **Update the Versioned Definition**
|
||||
|
||||
Update the versioned `.msg` topic message file (or `.srv` service message file) with required changes.
|
||||
|
||||
First increment the `MESSAGE_VERSION` field.
|
||||
Then update the message fields that prompted the version change.
|
||||
|
||||
For example, update `msg/versioned/VehicleAttitude.msg` from:
|
||||
|
||||
```.msg
|
||||
uint32 MESSAGE_VERSION = 3
|
||||
uint64 timestamp
|
||||
...
|
||||
```
|
||||
|
||||
to
|
||||
|
||||
```.msg
|
||||
uint32 MESSAGE_VERSION = 4 # Increment
|
||||
uint64 timestamp
|
||||
float32 new_field # Make definition changes
|
||||
...
|
||||
```
|
||||
|
||||
4. **Add a New Translation Header**
|
||||
|
||||
Add a new version translation to bridge the archived version and the updated current version, by creating a new translation header.
|
||||
|
||||
For example, create a direct translation header `translation_node/translations/translation_vehicle_attitude_v4.h`:
|
||||
|
||||
```c++
|
||||
// Translate VehicleAttitude v3 <--> v4
|
||||
#include <px4_msgs_old/msg/vehicle_attitude_v3.hpp>
|
||||
#include <px4_msgs/msg/vehicle_attitude.hpp>
|
||||
|
||||
class VehicleAttitudeV4Translation {
|
||||
public:
|
||||
using MessageOlder = px4_msgs_old::msg::VehicleAttitudeV3;
|
||||
static_assert(MessageOlder::MESSAGE_VERSION == 3);
|
||||
|
||||
using MessageNewer = px4_msgs::msg::VehicleAttitude;
|
||||
static_assert(MessageNewer::MESSAGE_VERSION == 4);
|
||||
|
||||
static constexpr const char* kTopic = "fmu/out/vehicle_attitude";
|
||||
|
||||
static void fromOlder(const MessageOlder &msg_older, MessageNewer &msg_newer) {
|
||||
msg_newer.timestamp = msg_older.timestamp;
|
||||
msg_newer.timestamp_sample = msg_older.timestamp_sample;
|
||||
msg_newer.q[0] = msg_older.q[0];
|
||||
msg_newer.q[1] = msg_older.q[1];
|
||||
msg_newer.q[2] = msg_older.q[2];
|
||||
msg_newer.q[3] = msg_older.q[3];
|
||||
msg_newer.delta_q_reset = msg_older.delta_q_reset;
|
||||
msg_newer.quat_reset_counter = msg_older.quat_reset_counter;
|
||||
|
||||
// Populate `new_field` with some value
|
||||
msg_newer.new_field = -1;
|
||||
}
|
||||
|
||||
static void toOlder(const MessageNewer &msg_newer, MessageOlder &msg_older) {
|
||||
msg_older.timestamp = msg_newer.timestamp;
|
||||
msg_older.timestamp_sample = msg_newer.timestamp_sample;
|
||||
msg_older.q[0] = msg_newer.q[0];
|
||||
msg_older.q[1] = msg_newer.q[1];
|
||||
msg_older.q[2] = msg_newer.q[2];
|
||||
msg_older.q[3] = msg_newer.q[3];
|
||||
msg_older.delta_q_reset = msg_newer.delta_q_reset;
|
||||
msg_older.quat_reset_counter = msg_newer.quat_reset_counter;
|
||||
|
||||
// Discards `new_field` from MessageNewer
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_TOPIC_TRANSLATION_DIRECT(VehicleAttitudeV4Translation);
|
||||
```
|
||||
|
||||
Version translation templates are provided here:
|
||||
|
||||
- [Direct Topic Message Translation Template](https://github.com/PX4/PX4-Autopilot/blob/main/msg/translation_node/translations/example_translation_direct_v1.h)
|
||||
- [Generic Topic Message Translation Template](https://github.com/PX4/PX4-Autopilot/blob/main/msg/translation_node/translations/example_translation_multi_v2.h)
|
||||
- [Direct Service Message Translation Template](https://github.com/PX4/PX4-Autopilot/blob/main/msg/translation_node/translations/example_translation_service_v1.h)
|
||||
|
||||
5. **Include New Headers in `all_translations.h`**
|
||||
|
||||
Add all newly created headers to [`translations/all_translations.h`](https://github.com/PX4/PX4-Autopilot/blob/main/msg/translation_node/translations/all_translations.h) so that the translation node can find them.
|
||||
|
||||
For example, append the following line to `all_translation.h`:
|
||||
|
||||
```c++
|
||||
#include "translation_vehicle_attitude_v4.h"
|
||||
```
|
||||
|
||||
Note that in the example above and in most cases, step 4 only requires the developer to create a direct translation for the definition change.
|
||||
This is because the changes only involved a single message.
|
||||
In more complex cases of splitting, merging and/or moving definitions then a generic translation must be created.
|
||||
|
||||
For example when moving a field from one message to another, a single generic translation should be added with the two older message versions as input, and the two newer versions as output.
|
||||
This ensures there is no information lost when translating forward or backward.
|
||||
|
||||
This is exactly the approach shown by the [Generic Topic Message Translation Template](https://github.com/PX4/PX4-Autopilot/blob/main/msg/translation_node/translations/example_translation_multi_v2.h), omitting only the code for actually modifying fields in the `fromOlder()` and `toOlder()` methods.
|
||||
|
||||
:::warning
|
||||
If a nested message definition changes, all messages including that message also require a version update.
|
||||
For example this would be the case for message [PositionSetpointTriplet](../msg_docs/PositionSetpointTriplet.md) if it were versioned.
|
||||
This is primarily important for services which are more likely reference other message definitions.
|
||||
:::
|
||||
|
||||
## Implementation Details
|
||||
|
||||
The translation node dynamically monitors the topics and services.
|
||||
It then instantiates the counterside of the publications and subscribers as required.
|
||||
For example if there is an external publisher for version 1 of a topic and subscriber for version 2.
|
||||
|
||||
Internally, it maintains a graph of all known topic and version tuples (which are the graph nodes).
|
||||
The graph is connected by the message translations.
|
||||
As arbitrary message translations can be registered, the graph can have cycles and multiple paths from one node to another.
|
||||
Therefore on a topic update, the graph is traversed using a shortest path algorithm.
|
||||
When moving from one node to the next, the message translation method is called with the current topic data.
|
||||
If a node contains an instantiated publisher (because it previously detected an external subscriber), the data is published.
|
||||
Thus, multiple subscribers of any version of the topic can be updated with the correct version of the data.
|
||||
|
||||
For translations with multiple input topics, the translation continues once all input messages are available.
|
||||
|
||||
## Обмеження
|
||||
|
||||
- Translation of service messages does not work on ROS Humble, but does on ROS Jazzy.
|
||||
This is because the current implementation depends on a service API that is not yet available in ROS Humble.
|
||||
Translation of topic messages is fully supported.
|
||||
- Services messages only support a linear history, i.e. no message splitting or merging.
|
||||
- Having both publishers and subscribers for two different versions of the same topic is currently not handled by the translation node and would trigger infinite circular publications.
|
||||
This refers to the following problematic configuration:
|
||||
|
||||
```
|
||||
app 1: pub topic_v1, sub topic_v1
|
||||
app 2: pub topic_v2, sub topic_v2
|
||||
```
|
||||
|
||||
In practice this configuration is unlikely to occur because ROS topics shared with the FMU are intended to be directional (e.g. `/fmu/out/vehicle_status` or `/fmu/in/trajectory_setpoint`), therefore apps typically do not publish and subscribe simultaneously to the same topic.
|
||||
The translation node could be extended to handle this corner-case if required.
|
||||
|
||||
Original document with requirements: https://docs.google.com/document/d/18_RxV1eEjt4haaa5QkFZAlIAJNv9w5HED2aUEiG7PVQ/edit?usp=sharing
|
||||
@@ -0,0 +1,308 @@
|
||||
# Інтерфейс навігації PX4 ROS 2
|
||||
|
||||
<Badge type="tip" text="PX4 v1.15" /> <Badge type="warning" text="Experimental" />
|
||||
|
||||
:::warning
|
||||
Експериментальні налаштування
|
||||
На момент написання цієї статті, деякі частини бібліотеки інтерфейсу PX4 ROS 2 є експериментальними і, отже, можуть бути змінені.
|
||||
:::
|
||||
|
||||
[PX4 ROS 2 Interface бібліотека](../ros2/px4_ros2_interface_lib. d) інтерфейс навігації дозволяє розробникам надсилати дані щодо позиції PX4 безпосередньо з ROS 2 додатків, така як система VIO або система відповідності мап.
|
||||
Цей інтерфейс надає шар абстракції від PX4 та каркасу обміну повідомленнями uORB і вводить деякі перевірки на розумність стану оцінки оновлень, надісланих через інтерфейс.
|
||||
Ці вимірювання потім об'єднуються в EKF так само, як внутрішні вимірювання PX4.
|
||||
|
||||
Бібліотека надає два класи: LocalPositionMeasurementInterface та GlobalPositionMeasurementInterface, які обидва використовують схожий метод оновлення для надання оновлення локальної позиції або глобальної позиції до PX4 відповідно.
|
||||
Метод `update` очікує від позиції вимірювання `struct` ([`LocalPositionMeasurement`](https://auterion.github.io/px4-ros2-interface-lib/structpx4__ros2_1_1__ros2_1_LocalPositionMeasurement.html) або [`GlobalPositionMeasurement`](https://auterion.github.io/px4-ros2-interface-lib/structpx__ros2_1_GlobalitionMeasurement.html)), які розробники можуть народитися власними вимірюваннями.
|
||||
|
||||
## Установка та перший тест
|
||||
|
||||
Для початку роботи потрібно виконати наступні кроки:
|
||||
|
||||
1. Make sure you have a working [ROS 2 setup](../ros2/user_guide.md), with [`px4_msgs`](https://github.com/PX4/px4_msgs) in the ROS 2 workspace.
|
||||
|
||||
2. Клонуйте репозиторій в робочий простір:
|
||||
|
||||
```sh
|
||||
cd $ros_workspace/src
|
||||
git clone --recursive https://github.com/Auterion/px4-ros2-interface-lib
|
||||
```
|
||||
|
||||
:::info
|
||||
Для забезпечення сумісності, використовуйте останні _main_ гілки для PX4, _px4_msgs_ та бібліотеки.
|
||||
Дивіться також [here](https://github.com/Auterion/px4-ros2-interface-lib#compatibility-with-px4).
|
||||
|
||||
:::
|
||||
|
||||
3. Побудуйте робочий простір:
|
||||
|
||||
```sh
|
||||
cd ..
|
||||
colcon build
|
||||
```
|
||||
|
||||
4. У іншій оболонці запустіть PX4 SITL:
|
||||
|
||||
```sh
|
||||
cd $px4-autopilot
|
||||
make px4_sitl gazebo-classic
|
||||
```
|
||||
|
||||
(тут ми використовуємо Gazebo-Classic, але ви можете використовувати будь-яку модель або симулятор)
|
||||
|
||||
5. У іншій оболонці запустіть агента micro XRCE (ви можете залишити його запущеним після цього):
|
||||
|
||||
```sh
|
||||
MicroXRCEAgent udp4 -p 8888
|
||||
```
|
||||
|
||||
6. Поверніться до терміналу ROS 2, створіть робочу область, яку ви щойно створили (у кроці 3), і запустіть приклад [global_navigation](https://github.com/Auterion/px4-ros2-interface-lib/tree/main/examples/cpp/navigation/global_navigation), який періодично надсилає фіктивні оновлення глобальної позиції:
|
||||
|
||||
```sh
|
||||
source install/setup.bash
|
||||
ros2 run example_global_navigation_cpp example_global_navigation
|
||||
```
|
||||
|
||||
Ви повинні отримати вивід, подібний до цього, що показує, що глобальний інтерфейс успішно надсилає оновлення позиції:
|
||||
|
||||
```sh
|
||||
[INFO] [1702030701.836897756] [example_global_navigation_node]: example_global_navigation_node running!
|
||||
[DEBUG] [1702030702.837279784] [example_global_navigation_node]: Successfully sent position update to navigation interface.
|
||||
[DEBUG] [1702030703.837223884] [example_global_navigation_node]: Successfully sent position update to navigation interface.
|
||||
```
|
||||
|
||||
7. У PX4 оболонці можна перевірити, що PX4 отримує глобальні оновлення позиції:
|
||||
|
||||
```sh
|
||||
listener aux_global_position
|
||||
```
|
||||
|
||||
Вихід має містити:
|
||||
|
||||
```sh
|
||||
TOPIC: aux_global_position
|
||||
aux_global_position
|
||||
timestamp: 46916000 (0.528000 seconds ago)
|
||||
timestamp_sample: 46916000 (0 us before timestamp)
|
||||
lat: 12.343210
|
||||
lon: 23.454320
|
||||
alt: 12.40000
|
||||
alt_ellipsoid: 0.00000
|
||||
delta_alt: 0.00000
|
||||
eph: 0.31623
|
||||
epv: 0.44721
|
||||
terrain_alt: 0.00000
|
||||
lat_lon_reset_counter: 0
|
||||
alt_reset_counter: 0
|
||||
terrain_alt_valid: False
|
||||
dead_reckoning: False
|
||||
```
|
||||
|
||||
8. Тепер ви готові використовувати навігаційний інтерфейс для надсилання своїх оновлень.
|
||||
|
||||
## Як користуватися бібліотекою
|
||||
|
||||
Для надсилання вимірювання позиції ви заповнюєте структуру позиції з виміряними значеннями.
|
||||
Потім викликаєте функцію оновлення інтерфейсу з цією структурою як аргументом.
|
||||
|
||||
Для базового прикладу, як користуватися цим інтерфейсом, ознайомтеся з [examples](https://github.com/Auterion/px4-ros2-interface-lib/tree/main/examples/cpp/navigation) в `Auterion/px4-rosface-lib` репозиторію, наприклад [examples/cpp/navigation/local_navigation](https://github.com/Auterion/px4-ros2-interface-lib/b/main/examples/cpp/navigation/local_navigation/inclation/inclde/local_localation.hppation.hpp) або examples/cpps/cppation/globation](https://github.com/Auter/intertere-face-face-face-facb/mainb/mppation/mppation/example/example/navigation/navigation/navigation/navig/navigation/navigation/navig/navig/navig/navighblob/navig
|
||||
|
||||
### Оновлення локальної позиції
|
||||
|
||||
Спочатку переконайтеся, що параметр PX4 [`EKF2_EV_CTRL`](../advanced_config/parameter_reference.md#EKF2_EV_CTRL) налаштований належним чином для ефективного використання зовнішніх локальних вимірів, встановивши відповідні біти в `true`:
|
||||
|
||||
- 0: Дані горизонтальної позиції
|
||||
- 1: Дані вертикальної позиції
|
||||
- 2: Дані швидкості
|
||||
- 3: Дані кута yaw
|
||||
|
||||
|
||||
|
||||
1. Створіть [`LocalPositionMeasurementInterface`](https://auterion.github.io/px4-ros2-interface-lib/classpx4ros2_1_1__ros2_1_1LocalPositionMeasurementInterface.html), надаючи йому речі: ID ROS вузол, а також посилання на швидкість вашого вимірювання.
|
||||
2. Заповніть [`LocalPositionMeasurement`](https://auterion.github.io/px4-ros2-interface-lib/structpx4__ros2_1_1_1_LocalPositionMeasurement.html) `struct` своїми вимірюваннями.
|
||||
3. Передайте `struct` на `LocalPositionMeasurementInterface` [`update()`](https://auterion.github.io/px4-ros2-interface-lib/classpx4__ros2_1_1LocalPositionMeasureInterface.html#a6fd180b944710716d418b2cfe1c0c8e3) метод.
|
||||
|
||||
Доступні рамки посилання на положення та швидкість для ваших вимірювань визначаються наступним переліком:
|
||||
|
||||
```cpp
|
||||
enum class PoseFrame
|
||||
{
|
||||
Unknown,
|
||||
LocalNED,
|
||||
LocalFRD
|
||||
};
|
||||
|
||||
enum class VelocityFrame
|
||||
{
|
||||
Unknown,
|
||||
LocalNED,
|
||||
LocalFRD,
|
||||
BodyFRD
|
||||
};
|
||||
```
|
||||
|
||||
Структура `LocalPositionMeasment` визначено таким чином:
|
||||
|
||||
```cpp
|
||||
struct LocalPositionMeasurement
|
||||
{
|
||||
rclcpp::Time timestamp_sample {};
|
||||
|
||||
std::optional<Eigen::Vector2f> position_xy {std::nullopt};
|
||||
std::optional<Eigen::Vector2f> position_xy_variance {std::nullopt};
|
||||
std::optional<float> position_z {std::nullopt};
|
||||
std::optional<float> position_z_variance {std::nullopt};
|
||||
|
||||
std::optional<Eigen::Vector2f> velocity_xy {std::nullopt};
|
||||
std::optional<Eigen::Vector2f> velocity_xy_variance {std::nullopt};
|
||||
std::optional<float> velocity_z {std::nullopt};
|
||||
std::optional<float> velocity_z_variance {std::nullopt};
|
||||
|
||||
std::optional<Eigen::Quaternionf> attitude_quaternion {std::nullopt};
|
||||
std::optional<Eigen::Vector3f> attitude_variance {std::nullopt};
|
||||
};
|
||||
```
|
||||
|
||||
Метод `update()` глобального інтерфейсу очікує дотримання наступних умов для `GlobalPositionMeasment`:
|
||||
|
||||
- Час відбору вибіркових вимірювань визначений.
|
||||
- Значення не мають NAN\`\`.
|
||||
- Якщо надано значення вимірювання, його відповідне значення розбіжності добре визначено (наприклад, якщо `lat_lon` визначено, то необхідно вказати `horizontal_variance`).
|
||||
- Якщо надано значення вимірювання, його пов'язана рамка посилання не є невідомою (наприклад, якщо визначено `position_xy`, то інтерфейс було ініціалізовано з подовжувачем кадру, відмінного від `PoseFrame:Unknown`).
|
||||
|
||||
Наступний фрагмент коду є прикладом вузла ROS 2, який використовує локальний інтерфейс навігації для надсилання оновлень 3D-позиції у рамці посилання Північ-Схід-Дон (NED) до PX4:
|
||||
|
||||
```cpp
|
||||
class MyLocalMeasurementUpdateNode : public rclcpp::Node
|
||||
{
|
||||
public:
|
||||
MyLocalMeasurementUpdateNode()
|
||||
: Node("my_node_name")
|
||||
{
|
||||
// Set pose measurement reference frame to north-east-down
|
||||
const px4_ros2::PoseFrame pose_frame = px4_ros2::PoseFrame::LocalNED;
|
||||
// We will only send pose measurements in this example
|
||||
// Set velocity measurement reference frame to unknown
|
||||
const px4_ros2::VelocityFrame velocity_frame = px4_ros2::VelocityFrame::Unknown;
|
||||
// Initialize local interface [1]
|
||||
_local_position_measurement_interface =
|
||||
std::make_shared<px4_ros2::LocalPositionMeasurementInterface>(*this, pose_frame, velocity_frame);
|
||||
}
|
||||
|
||||
void sendUpdate()
|
||||
{
|
||||
while (running) { // Potentially make method run as a callback or on a timer
|
||||
// Generate local position measurement
|
||||
rclcpp::Time timestamp_sample = ...
|
||||
Eigen::Vector2f position_xy = ...
|
||||
Eigen::Vector2f position_xy_variance = ...
|
||||
float position_z = ...
|
||||
float position_z_variance = ...
|
||||
|
||||
// Populate the local position measurement struct [2]
|
||||
px4_ros2::LocalPositionMeasurement local_position_measurement{};
|
||||
local_position_measurement.timestamp_sample = timestamp_sample;
|
||||
local_position_measurement.position_xy = position_xy;
|
||||
local_position_measurement.position_xy_variance = position_xy_variance;
|
||||
local_position_measurement.position_z = position_z;
|
||||
local_position_measurement.position_z_variance = position_z_variance;
|
||||
|
||||
// Send measurement to PX4 using the interface [3]
|
||||
try {
|
||||
_local_position_measurement_interface->update(local_position_measurement);
|
||||
} catch (const px4_ros2::NavigationInterfaceInvalidArgument & e) {
|
||||
// Handle exceptions caused by invalid local_position_measurement definition
|
||||
RCLCPP_ERROR(get_logger(), "Exception caught: %s", e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<px4_ros2::LocalPositionMeasurementInterface> _local_position_measurement_interface;
|
||||
};
|
||||
```
|
||||
|
||||
###
|
||||
|
||||
Спочатку переконайтеся, що параметр PX4 [`EKF2_EV_CTRL`](../advanced_config/parameter_reference.md#EKF2_EV_CTRL) налаштований належним чином для ефективного використання зовнішніх локальних вимірів, встановивши відповідні біти в `true`:
|
||||
|
||||
- 0: Дані горизонтальної позиції
|
||||
- 1: Дані вертикальної позиції
|
||||
|
||||
Щоб надіслати глобальне вимірювання на PX4:
|
||||
|
||||
1. Створіть [`LocalPositionMeasurementInterface`](https://auterion.github.io/px4-ros2-interface-lib/classpx4ros2_1_1__ros2_1_1LocalPositionMeasurementInterface.html), надаючи йому речі: ID ROS вузол, а також посилання на швидкість вашого вимірювання.
|
||||
2. Заповніть [`LocalPositionMeasurement`](https://auterion.github.io/px4-ros2-interface-lib/structpx4__ros2_1_1_1_LocalPositionMeasurement.html) `struct` своїми вимірюваннями.
|
||||
3. Передайте `struct` на `LocalPositionMeasurementInterface` [`update()`](https://auterion.github.io/px4-ros2-interface-lib/classpx4__ros2_1_1LocalPositionMeasureInterface.html#a6fd180b944710716d418b2cfe1c0c8e3) метод.
|
||||
|
||||
Структуру `LocalPositionMeasment` визначено таким чином:
|
||||
|
||||
```cpp
|
||||
struct GlobalPositionMeasurement
|
||||
{
|
||||
rclcpp::Time timestamp_sample {};
|
||||
|
||||
std::optional<Eigen::Vector2d> lat_lon {std::nullopt};
|
||||
std::optional<float> horizontal_variance {std::nullopt};
|
||||
|
||||
std::optional<float> altitude_msl {std::nullopt};
|
||||
std::optional<float> vertical_variance {std::nullopt};
|
||||
};
|
||||
```
|
||||
|
||||
Метод `update()` глобального інтерфейсу очікує дотримання наступних умов для `GlobalPositionMeasment`:
|
||||
|
||||
-
|
||||
- Значення не мають NAN.
|
||||
- Якщо надано значення вимірювання, його відповідне значення розбіжності добре визначено (наприклад, якщо `lat_lon` визначено, то необхідно вказати `horizontal_variance`).
|
||||
|
||||
Наступний фрагмент коду є прикладом вузла ROS 2, який використовує локальний інтерфейс навігації для надсилання оновлень 3D-позиції у рамці посилання Північ-Схід-Дон (NED) до PX4:
|
||||
|
||||
```cpp
|
||||
class MyGlobalMeasurementUpdateNode : public rclcpp::Node
|
||||
{
|
||||
public:
|
||||
MyGlobalMeasurementUpdateNode()
|
||||
: Node("my_node_name")
|
||||
{
|
||||
// Initialize global interface [1]
|
||||
_global_position_measurement_interface =
|
||||
std::make_shared<px4_ros2::GlobalPositionMeasurementInterface>(*this);
|
||||
}
|
||||
|
||||
void sendUpdate()
|
||||
{
|
||||
while (running) { // Potentially make method run as a callback or on a timer
|
||||
// Generate global position measurement
|
||||
rclcpp::Time timestamp_sample = ...
|
||||
Eigen::Vector2d lat_lon = ...
|
||||
float horizontal_variance = ...
|
||||
float altitude_msl = ...
|
||||
float vertical_variance = ...
|
||||
|
||||
// Populate the global position measurement struct [2]
|
||||
px4_ros2::GlobalPositionMeasurement global_position_measurement{};
|
||||
global_position_measurement.timestamp_sample = timestamp_sample;
|
||||
global_position_measurement.lat_lon = lat_lon;
|
||||
global_position_measurement.horizontal_variance = horizontal_variance;
|
||||
global_position_measurement.altitude_msl = altitude_msl;
|
||||
global_position_measurement.vertical_variance = vertical_variance;
|
||||
|
||||
// Send measurement to PX4 using the interface [3]
|
||||
try {
|
||||
_global_position_measurement_interface->update(global_position_measurement);
|
||||
} catch (const px4_ros2::NavigationInterfaceInvalidArgument & e) {
|
||||
// Handle exceptions caused by invalid global_position_measurement definition
|
||||
RCLCPP_ERROR(get_logger(), "Exception caught: %s", e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<px4_ros2::GlobalPositionMeasurementInterface> _global_position_measurement_interface;
|
||||
};
|
||||
```
|
||||
|
||||
## Кілька екземплярів інтерфейсу
|
||||
|
||||
Використання кількох екземплярів одного інтерфейсу (напр. локально та локально) для надсилання повідомлень про розрахунок буде передавати всі оновлення до однієї теми і що призведе до перехресної розмови.
|
||||
Це не повинно впливати на об'єднання вимірювань в EKF, але різні джерела вимірювань стануть нерозрізненними.
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user