Writing a New Aerial Platform
Overview
This tutorial shows how to create your own Aerial Platform. To understand what is an AeroStack2 Aerial Platform, please revisit Aerial Platforms.
For better understanding, this tutorial will be a walktrough of one existing platform. Due to accesibility, Gazebo Platform will be the chosen one. The code used in this tutorial can be found in Github.
Note
Just as a reminder, an aerial platform has two sides. One who deals with AeroStack2 communication (ROS 2) and another who deals with the specific hardware platform. Since all platforms inherit from a common parent class, the user should only focus on how to communicate (send commands and receive data) with the platform hardware. ROS 2 communication will be done by the base class, who has all the subscribers and publishers needed in AeroStack2.
Requirements
ROS 2 Humble
AeroStack2
Gazebo Fortress
Quadrotor Gazebo model (as2_gazebo_assets, already included within AeroStack2)
Tutorial Steps
1. Abstract class, Aerial Platform
All Aerial Platforms should inherit from a common base class as2::AerialPlatform
, an
extension of a rclcpp::Node
. It provides the basic functionality for the platform and
is responsible for handling the platform state machine and the platform status. It also
handles the command subscriptions and the basic platform services.
The base class provides a set of virtual methods to implement a platform. It also offers a group of protected attributes where the data received from the ROS 2 side is received. The list of methods is presented in the table below:
Virtual method |
Method description |
Requires override? |
---|---|---|
configureSensors() |
Configures the platform sensors. |
Yes |
ownSendCommand() |
Handles how a command must be sended in a specific platform. |
Yes |
ownSetArmingState() |
Handles how arming state has to be settled in a specific platform. |
Yes |
ownSetOffboardControl() |
Handles how offboard mode has to be settled in a specific platform. |
Yes |
ownSetPlatformControlMode() |
Handles how the control mode has to be settled in a specific platform. |
Yes |
ownKillSwitch() |
Handles the platform emergency kill switch command (stop the motors inmediately). Can not be reversed. USE WITH CAUTION. |
Yes |
ownStopPlatform() |
Handles the platform emergency stop command. STOP means to hover as best as possible. This hover is different from the hover in the platform control mode. And when it is activated the platform will stop hearing commands from AS2. USE WITH CAUTION. |
Yes |
ownTakeoff() |
Handles the platform takeoff command. |
No |
ownLand() |
Handles the platform landing command. |
No |
Besides, the group of protected attributes are:
Attribute |
Type |
Description |
---|---|---|
|
geometry_msgs::msg::PoseStamped |
Pose msg received from |
|
geometry_msgs::msg::TwistStamped |
Twist msg received from |
|
as2_msgs::msg::Thrust |
Pose msg received from |
|
as2_msgs::msg::PlatformInfo |
Platform information data container. Readable only. |
2. Overriden methods
Class constuctor
Besides the overriden methods, it is necessary to create all the interface communication with the hardware specific hardware. In this case, only two publishers are needed to arm and send commands to de platform.
GazeboPlatform::GazeboPlatform() : as2::AerialPlatform() {
this->declare_parameter<std::string>("cmd_vel_topic"); // Reading topic from launch argument
std::string cmd_vel_topic_param = this->get_parameter("cmd_vel_topic").as_string();
this->declare_parameter<std::string>("arm_topic"); // Reading topic from launch argument
std::string arm_topic_param = this->get_parameter("arm_topic").as_string();
twist_pub_ = this->create_publisher<geometry_msgs::msg::Twist>(cmd_vel_topic_param, rclcpp::QoS(1));
arm_pub_ = this->create_publisher<std_msgs::msg::Bool>(arm_topic_param, rclcpp::QoS(1));
}
Sensor configuration
There is no need to call this method manually. Base class will call it after instantiation.
void GazeboPlatform::configureSensors() {
// Not sensors used within the platform
}
In this specific platform, there are no need of configure any sensors due to ros-gazebo bridge, which is publishing directly the sensors in ROS topics. You can find more information about this at Gazebo Platform.
Note
TDB: Reference how to use Sensors
Control mode
When a control mode reach this method, it can be assumed that the control mode is supported by the platform (see 3. Control modes configuration).
bool GazeboPlatform::ownSetPlatformControlMode(const as2_msgs::msg::ControlMode &control_in) {
RCLCPP_INFO(this->get_logger(), "Control mode: [%s]",
as2::control_mode::controlModeToString(control_in).c_str());
control_in_ = control_in; // storing control mode
return true;
}
Note
TDB: Reference how to control modes work
Send command
Twist msg received from actuator_command/twist
topic is sent to the simulated drone. Before,
current control mode is checked to send null speed if HOVER mode is active.
bool GazeboPlatform::ownSendCommand() {
if (control_in_.control_mode == as2_msgs::msg::ControlMode::HOVER) {
geometry_msgs::msg::Twist twist_msg;
twist_msg.linear.x = 0;
twist_msg.linear.y = 0;
twist_msg.linear.z = 0;
twist_msg.angular.x = 0;
twist_msg.angular.y = 0;
twist_msg.angular.z = 0;
twist_pub_->publish(twist_msg);
return true;
}
twist_pub_->publish(command_twist_msg_.twist);
return true;
}
Warning
Notice that the sending command msg might need some transformations from the one stored in
command_twist_msg_
attribute. In this particular example, we are not performing any
transformation since the control mode has the same reference frame that the expected by
the platform. Remember that the controller publish the command in the reference frame
specified in the platform control mode.
Arming
Send arming message to Gazebo simulator.
bool GazeboPlatform::ownSetArmingState(bool state) {
std_msgs::msg::Bool arm_msg;
arm_msg.data = state;
arm_pub_->publish(arm_msg);
resetCommandTwistMsg();
return true;
}
Offboard
Setting offboard mode before fly is not needed on Gazebo simulator.
bool GazeboPlatform::ownSetOffboardControl(bool offboard) {
return true; // Offboard not needed
}
Emergency kill switch
Disarms the drone.
void GazeboPlatform::ownKillSwitch() {
ownSetArmingState(false);
return;
}
Emergency stop
Sends null speed to the drone.
void GazeboPlatform::ownStopPlatform() {
geometry_msgs::msg::Twist twist_msg_hover;
twist_msg_hover.linear.x = 0.0;
twist_msg_hover.linear.y = 0.0;
twist_msg_hover.linear.z = 0.0;
twist_msg_hover.angular.x = 0.0;
twist_msg_hover.angular.y = 0.0;
twist_msg_hover.angular.z = 0.0;
twist_pub_->publish(twist_msg_hover);
return;
}
Takeoff
Gazebo simulator doesn’t not have a platform takeoff. Call platform takeoff here if supported.
bool GazeboPlatform::ownTakeoff() {
RCLCPP_WARN(this->get_logger(), "Takeoff platform not enabled");
return false;
}
Land
Gazebo simulator doesn’t not have a platform land. Call platform land here if supported.
bool GazeboPlatform::ownLand() {
RCLCPP_WARN(this->get_logger(), "Land platform not enabled");
return false;
}
3. Control modes configuration
Valid control modes are defined in a configuration file control_modes.yaml
. In the case of
Gazebo simulator, only two control modes are valid:
available_modes:
# - 0b00000000 # UNSET
- 0b00010000 # HOVER
# - 0b00100100 # ACRO (p,q,r, Thrust)
# - 0b00110001 # ATTITUDE with yaw ANGLE ( r,p,y , Thrust)
# - 0b00110101 # ATTITUDE with yaw SPEED ( r,p, dy , Thrust)
# - 0b01000000 # SPEED with yaw ANGLE in the LOCAL_FLU_FRAME
# - 0b01000001 # SPEED with yaw ANGLE in the GLOBAL_ENU_FRAME
- 0b01000100 # SPEED with yaw SPEED in the LOCAL_FLU_FRAME
# - 0b01000101 # SPEED with yaw SPEED in the GLOBAL_ENU_FRAME
# - 0b01010000 # SPEED_IN_A_PLANE with yaw ANGLE in the LOCAL_FLU_FRAME
# - 0b01010001 # SPEED_IN_A_PLANE with yaw ANGLE in the GLOBAL_ENU_FRAME
# - 0b01010100 # SPEED_IN_A_PLANE with yaw SPEED in the LOCAL_FLU_FRAME
# - 0b01010101 # SPEED_IN_A_PLANE with yaw SPEED in the GLOBAL_ENU_FRAME
# - 0b01100001 # POSITION with yaw ANGLE in the GLOBAL_ENU_FRAME
# - 0b01100101 # POSITION with yaw SPEED in the GLOBAL_ENU_FRAME
# - 0b01110001 # TRAJECTORY with yaw ANGLE in the GLOBAL_ENU_FRAME
# - 0b01110101 # TRAJECTORY with yaw SPEED in the GLOBAL_ENU_FRAME
This configuration explains why no extra comprobation is performed in
ownSetPlatformControlMode()
and why before sending commands only HOVER mode is
checked (ownSendCommand()
).
Note
TDB: Reference how to control modes work