多旋翼无人机仿真 rotors_simulator:用键盘控制无人机飞行

news2025/1/13 3:02:26

多旋翼无人机仿真 rotors_simulator:用键盘控制无人机飞行

  • 前言
    • 书接上文
  • 接口测试
  • 键盘指令发布
  • 指令转换与发布
  • 修改 rotors_simulator 的控制接口节点
  • 测试

前言

RotorS 是一个MAV gazebo 仿真系统。

提供了几种多旋翼仿真模型,例如

  • AscTec Hummingbird
  • AscTec Pelican
  • AscTec Firefly

但是仿真系统不限于使用这几种模型

AscTec 是 德国Ascending Technologies公司的缩写。

是很早的无人机了,实物张下面这个样子:

在这里插入图片描述

仿真系统中包含很多种仿真传感器,都可以安装在无人机上,例如:

  • IMU
  • 里程计
  • 视觉惯导相机

功能包中包含了几种控制器,包含位置控制,游戏手柄控制灯

github的地址为:https://github.com/ethz-asl/rotors_simulator

书接上文

在上一篇博客中 : https://www.guyuehome.com/40362 分析了 rotors_simulator 自带的一个控制接口
需要输入 roll pitch yawrate thrust 的指令即可控制无人机的飞行。

但是在那篇文章也说了,它自带的键盘控制的节点功能启动不成功,导致无法在gazebo里控制无人机飞。

本篇博客将基于上篇分析的控制接口,再写一个键盘的指令发布功能,对应到接口指令的转换,来控制无人机先飞起来。

接口测试

RollPitchYawrateThrustControllerNode.cpp

送到控制器的 控制指令变量类型是这个
mav_msgs::EigenRollPitchYawrateThrust
这变量类型的定义是这样的
在这里插入图片描述
所以推力是 三维 的

给z轴一个推力,那么可以这样写
在里程计的回调函数里

  mav_msgs::EigenRollPitchYawrateThrust roll_pitch_yawrate_thrust;
    // 先自己固定一个控制量
  roll_pitch_yawrate_thrust.roll = 0.1;
  roll_pitch_yawrate_thrust.thrust.z() = 15;

  // 送入控制器中
  roll_pitch_yawrate_thrust_controller_.SetRollPitchYawrateThrust(roll_pitch_yawrate_thrust);

相当于 给一直给 z轴一个15的推力 期望的横滚角是0.1 弧度

可看到飞机横着飞了起来
在这里插入图片描述

ok,接口测通了,那么可自己写个键盘的指令发送器了。

键盘指令发布

键盘的指令发布有很多的现成功能包

我使用的最多的就是 teleop_twist_keyboard 这个功能包。

运行的时候终端会出现下面的界面:
在这里插入图片描述
从输出的信息上可以看出,其功能是发布Twist的速度控制指令。

其源码如下:

#!/usr/bin/env python

from __future__ import print_function

import roslib; roslib.load_manifest('teleop_twist_keyboard')
import rospy

from geometry_msgs.msg import Twist

import sys, select, termios, tty

msg = """
Reading from the keyboard  and Publishing to Twist!
---------------------------
Moving around:
   u    i    o
   j    k    l
   m    ,    .

For Holonomic mode (strafing), hold down the shift key:
---------------------------
   U    I    O
   J    K    L
   M    <    >

t : up (+z)
b : down (-z)

anything else : stop

q/z : increase/decrease max speeds by 10%
w/x : increase/decrease only linear speed by 10%
e/c : increase/decrease only angular speed by 10%

CTRL-C to quit
"""

moveBindings = {
        'i':(1,0,0,0),
        'o':(1,0,0,-1),
        'j':(0,0,0,1),
        'l':(0,0,0,-1),
        'u':(1,0,0,1),
        ',':(-1,0,0,0),
        '.':(-1,0,0,1),
        'm':(-1,0,0,-1),
        'O':(1,-1,0,0),
        'I':(1,0,0,0),
        'J':(0,1,0,0),
        'L':(0,-1,0,0),
        'U':(1,1,0,0),
        '<':(-1,0,0,0),
        '>':(-1,-1,0,0),
        'M':(-1,1,0,0),
        't':(0,0,1,0),
        'b':(0,0,-1,0),
    }

speedBindings={
        'q':(1.1,1.1),
        'z':(.9,.9),
        'w':(1.1,1),
        'x':(.9,1),
        'e':(1,1.1),
        'c':(1,.9),
    }

def getKey():
    tty.setraw(sys.stdin.fileno())
    select.select([sys.stdin], [], [], 0)
    key = sys.stdin.read(1)
    termios.tcsetattr(sys.stdin, termios.TCSADRAIN, settings)
    return key


def vels(speed,turn):
    return "currently:\tspeed %s\tturn %s " % (speed,turn)

if __name__=="__main__":
    settings = termios.tcgetattr(sys.stdin)

    pub = rospy.Publisher('cmd_vel', Twist, queue_size = 1)
    rospy.init_node('teleop_twist_keyboard')

    speed = rospy.get_param("~speed", 0.5)
    turn = rospy.get_param("~turn", 1.0)
    x = 0
    y = 0
    z = 0
    th = 0
    status = 0

    try:
        print(msg)
        print(vels(speed,turn))
        while(1):
            key = getKey()
            if key in moveBindings.keys():
                x = moveBindings[key][0]
                y = moveBindings[key][1]
                z = moveBindings[key][2]
                th = moveBindings[key][3]
            elif key in speedBindings.keys():
                speed = speed * speedBindings[key][0]
                turn = turn * speedBindings[key][1]

                print(vels(speed,turn))
                if (status == 14):
                    print(msg)
                status = (status + 1) % 15
            else:
                x = 0
                y = 0
                z = 0
                th = 0
                if (key == '\x03'):
                    break

            twist = Twist()
            twist.linear.x = x*speed; twist.linear.y = y*speed; twist.linear.z = z*speed;
            twist.angular.x = 0; twist.angular.y = 0; twist.angular.z = th*turn
            pub.publish(twist)

    except Exception as e:
        print(e)

    finally:
        twist = Twist()
        twist.linear.x = 0; twist.linear.y = 0; twist.linear.z = 0
        twist.angular.x = 0; twist.angular.y = 0; twist.angular.z = 0
        pub.publish(twist)

        termios.tcsetattr(sys.stdin, termios.TCSADRAIN, settings)

通过查看源码可知,该功能包最终通过扫描键盘按键,
最终发布 topic 名称为 cmd_vel 的消息
消息的格式为:geometry_msgs::Twist

指令转换与发布

由于键盘指令发布功能包 发布的命令和rotors_simulator我想用的控制接口的命令不一致

所以需要写一个节点来进行控制指令的转换

下面给出指令转换节点的 核心思想和重要代码

    ros::Subscriber keyboard_cmd_vel_sub_;

声明订阅句柄

keyboard_cmd_vel_sub_ = nh.subscribe("/cmd_vel",1,&PidPositionControllerNode::KeyboardCmdVelCallback,this);

订阅句柄赋值 /cmd_vel 就是键盘指令发布节点发布的topic的名称

然后实现其回调函数

    void KeyboardCmdVelCallback(const geometry_msgs::TwistConstPtr& cmdvel_msg)
    {
        // 取出指令
        geometry_msgs::Twist cmd_vel = *cmdvel_msg;

        roll =gain_roll* cmd_vel.linear.x ;
        pitch = gain_pitch*  cmd_vel.linear.y ;
        thrust = gain_thrust*  cmd_vel.linear.z ;
        yawrate = gain_yawrate*  cmd_vel.angular.z ;
    }

将指令转换成 roll pitch thrust yawrate的指令

    ros::Publisher Control_RollPitchYawrateThrust_pub_;

声明转换后指令的发布句柄

   Control_RollPitchYawrateThrust_pub_ =  nh.advertise<mav_msgs::RollPitchYawrateThrust>("control_keyboard_cmd",1);

赋值句柄,将发布的消息名称定义为:control_keyboard_cmd

    void PubControlMsg()
    {
        // 控制量
        mav_msgs::RollPitchYawrateThrust roll_pitch_yawrate_thrust;
        roll_pitch_yawrate_thrust.thrust.z= thrust_z;
        roll_pitch_yawrate_thrust.roll = roll_;
        roll_pitch_yawrate_thrust.pitch = pitch_;
        roll_pitch_yawrate_thrust.yaw_rate = yawrate_;
        Control_RollPitchYawrateThrust_pub_.publish(roll_pitch_yawrate_thrust);
    }

最后写一个发布指令msg的函数,完成对接口指令的发布

修改 rotors_simulator 的控制接口节点

下面需要做的就是修改rotors_simulator的控制接口节点
也就是roll_pitch_yawrate_thrust_controller_node.cpp

  ros::Subscriber Control_RollPitchYawrateThrust_sub_;

声明订阅指令的句柄

  Control_RollPitchYawrateThrust_sub_ = nh.subscribe("/control_pid_pos", 1,
                                     &RollPitchYawrateThrustControllerNode::ControlCallback, this);

赋值订阅句柄,消息名称要是 control_keyboard_cmd 和上面的能对应上

// 控制指令回调函数
void RollPitchYawrateThrustControllerNode::ControlCallback(
    const mav_msgs::RollPitchYawrateThrustConstPtr& roll_pitch_yawrate_thrust_reference_msg) {

  // 转成eigen的格式
  mav_msgs::EigenRollPitchYawrateThrust roll_pitch_yawrate_thrust;
  mav_msgs::eigenRollPitchYawrateThrustFromMsg(*roll_pitch_yawrate_thrust_reference_msg, &roll_pitch_yawrate_thrust);

  // 送入控制器中
  roll_pitch_yawrate_thrust_controller_.SetRollPitchYawrateThrust(roll_pitch_yawrate_thrust);
}

在回调函数中,将接收的指令,完成对控制器的输送。

测试

下面则可以打开 gazebo 和控制节点、键盘发布节点和指令转换节点,来进行测试了。

在这里插入图片描述

无人机可以按照期望的指令飞行。
但是无人机非常的不好控制,比如上下需要不断的改变推力,使得无人机高度保持。
也需要不断的改变姿态,使得无人机不至于飘的太远。

这是因为控制接口实现的仅是姿态控制,垂直上,没有实现闭环控制。
下一节将基于roll pitch yawrate thrust 控制接口,并订阅无人机里程计数据,实现pid闭环控制。使得无人机飞行更加稳定。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/116.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PHP反序列化

序列化与反序列化 序列化 反序列是指把对象转换为字符串的过程&#xff0c;便于在内存、文件、数据库中保存、传输&#xff0c;PHP中使用serialize函数进行序列化。 <?phpclass Person{public $name"php";protected $id;private $age;}$a new Person();$a_se…

全排列笔记

14天阅读挑战赛 全排列 题目 给定一个 没有重复 数字的序列&#xff0c;返回其所有可能的全排列。 示例: 输入: [1,2,3] 输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ] 解答 方法一&#xff1a;回溯 思路 从高中的数学知识我们可以知道 从[1,2,3…

如何在Linux上优雅地写代码-Linux生存指南

初入Linux&#xff0c;发现老是要面对一个命令行&#xff0c;大黑框&#xff0c;看不懂各种手册&#xff0c;写代码也是用vi/vim&#xff0c;难受的捉急。其实Linux下的各种工具&#xff0c;强大得超出你的想象&#xff0c;如果你初入Linux&#xff0c;那么你急需阅读这篇文章&…

操作系统的主要功能

目录 一. 处理机管理功能 1.1 进程控制 1.2 进程同步 1.3 进程通信 1.4 进程调度 二. 存储器管理功能 2.1 内存分配 2.2 内存保护 2.3 地址映射 2.4 内存扩充 三. 设备管理功能 3.1 缓冲管理 3.2 设备分配 3.3 设备处理 3.4 设备独立性和虚拟设备 四…

关于Python爬虫兼职,这里有一条高效路径

前言 昨天&#xff0c;一位00后前来报喜&#xff0c;也表达感谢。 他说&#xff0c;当初刚毕业啥也不会也找不到工作&#xff0c;最后听了我的&#xff0c;边学爬虫边做兼职项目&#xff0c;积极主动求职投简历&#xff0c;既可以兼职获得收益&#xff0c;也能积累项目经验谋求…

Linux:以K、M、G查看文件大小;

简介&#xff1a;灵活多变的查看文件的大小 历史攻略&#xff1a; Linux&#xff1a;sudo免密 python&#xff1a;执行dos命令、Linux命令 案例源码&#xff1a; # 以适当方式显示文件大小&#xff1a; ls -lh# 以byte显示文件大小&#xff1a; ls -l# 以M显示文件大小&am…

NR PUSCH(五) DMRS

微信同步更新欢迎关注同名modem协议笔记 PUSCH DMRS和PDSCH DMRS内容基本一样&#xff0c;但也有不同的地方&#xff0c;例如PUSCH 可能需要Transform precoding&#xff0c;port 对应0~11(DMRS configured type2)等等。先简单看看Transformprecoding的相关内容&#xff0c;Tr…

Excel数据分析实战之开宗明义: Excel与数据分析实战

大家好&#xff0c;我是爱编程的喵喵。双985硕士毕业&#xff0c;现担任全栈工程师一职&#xff0c;热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。喜欢通过博客创作的方式对所学的知识进行总结…

军用大数据 - Spark机器学习

文章目录第1关&#xff1a;Iris 分类任务描述相关知识1&#xff1a;观察数据集2&#xff1a;RFormula 特征提取3&#xff1a;pandas 的 concat 函数编程要求代码实现————————————————————————————————————————第2关&#xff1a;图片识…

网络原理 --- 传输层Ⅲ TCP协议中的滑动窗口,流量控制和拥塞控制

文章目录网络原理传输层TCP协议4.滑动窗口5.流量控制6.拥塞控制总结网络原理 介绍TCP/IP协议中每一层里面的核心内容~ 应用层传输层网络层数据链路层物理层 传输层TCP协议 4.滑动窗口 TCP能够保证可靠传输,但是失去了效率! 但是TCP希望能够在保证可靠性的前提下,尽可能地提…

达梦数据库在不修改SQL的情况下为SQL指定HINT

前言 在Oracle中可以使用outline、SQL PROFILE等手段去在无需修改SQL语句的情况下&#xff0c;来保证SQL执行计划在不同硬件环境下相同&#xff0c;从而保证SQL语句在不同环境的执行效率。那么&#xff0c;在达梦数据库中则可以使用SF_INJECT_HINT系统函数达到类似的效果。 SF…

Java学习笔记 --- 异常

一、基本介绍 Java语言中&#xff0c;将程序执行中发生的不正常情况称为“异常”。&#xff08;开发过程中的语法错误和逻辑错误不是异常&#xff09; 执行过程中所发生的异常事件可以分为两类 1、Error&#xff08;错误&#xff09;&#xff1a;Java虚拟机无法解决的严重问…

十月了,请问2022届的同学们都找到工作了吗?

今年的就业大环境就不多说了&#xff0c;大家都知道。一边是超千万规模的应届毕业生&#xff0c;叠加教培、地产等行业裁员&#xff1b;另一边则是疫情反复影响之下&#xff0c;企业瘦身裁员、停招、缩招。在白领性质的劳动力市场&#xff0c;劳动力供给严重大于需求&#xff0…

【C语言】解题训练

目录 字符串左旋 方法1 方法2 字符串旋转结果判断 方法1 方法2 杨氏矩阵 位段 题目1 题目2 联合体 题目1 题目2 有序序列合并 变种水仙花 找单身狗 字符串左旋 实现一个函数&#xff0c;可以左旋字符串中的k个字符。 例如&#xff1a; ABCD左旋一个字符得到…

纷享销客联合B.P商业伙伴携手30+企业CEO走进南天信息

数字化智能化建设的当下&#xff0c;数字化服务商承担着承上启下的核心力量。企业数字化转型成为刚需&#xff0c;意味着ICT企业的市场前景持续乐观&#xff0c;但在疫情和竞争加剧之下&#xff0c;企业发展也遭遇增长的挑战&#xff0c;如何在数字中国的趋势之下&#xff0c;乘…

大学网课搜题公众号系统

大学网课搜题公众号系统 本平台优点&#xff1a; 多题库查题、独立后台、响应速度快、全网平台可查、功能最全&#xff01; 1.想要给自己的公众号获得查题接口&#xff0c;只需要两步&#xff01; 2.题库&#xff1a; 题库&#xff1a;题库后台&#xff08;点击跳转&#xf…

【从小白到大白05】c和c++内存管理

c和c内存管理 文章目录c和c内存管理c内存管理方式new/delete操作内置类型new申请动态空间delete释放空间new和delete操作自定义类型operator new与operator delete函数new[]和delete[]定位new&#xff08;placement-new&#xff09;总结以上内存泄露以上就是全部内容啦&#xf…

WPS-JS宏开发-基础知识-03-三大基本结构

系统&#xff1a;Windows 11 软件&#xff1a;WPS表格11 本系列介绍一款类Excel的软件&#xff0c;WPS表格当然也是介绍其宏开发&#xff0c;不同的是&#xff0c;使用的JS宏会同样介绍多个系列&#xff0c;本系列介绍一些基础知识 Part 1&#xff1a; 三大逻辑结构 一个具体的…

如何给字符串字段加索引?

1.引例 现在的系统中&#xff0c;很多都会包含邮箱字段&#xff0c;那要如何给这个字段建立索引呢&#xff1f; 假设&#xff0c;现在维护了一个用户表&#xff0c;其中包含邮箱&#xff0c;定义如下&#xff1a; mysql>create table SUser(ID int primary key,email var…

OpenGL之多边形偏移、雾效、纹理映射

1.1 OpenGL中可以设置物体的点、线、面绘制模式。如果需要同时绘制多种模式&#xff0c;如下以面和线模式绘制两遍模型&#xff0c;可以看到线不连续&#xff0c;当镜头推远推近时会出现闪烁现象。 void glPolygonMode(GLenum face,GLenum mode);face :GL_FRONT&#xff0c;GL…