欢迎您访问365答案网,请分享给你的朋友!
生活常识 学习资料

px4+mavros+offboard控制+gazebo仿真以及与pixhawk4飞控连接(一)

时间:2023-07-25

 记录一下自己从px4到板载控制的一系列流程以及踩坑过程,希望也可以帮助大家避雷。话不多说直接上干货。

一、软、硬件平台

px4版本:1.10.1

飞控板:pixhawk4

ros版本:melodic

linux版本:ubuntu18.04.05

二、px4下载、编译、环境配置

推荐直接到官网下载,一次不成功就多试几次,也可以换github的国内镜像站:

https://github.com.cnpmjs.org

https://hub.fastgit.org

下载完成之后在linux下配置环境:

1.先将串口加入到用户组,增加串口权限

sudo usermod -a -G dialout $USER

2.首先运行firmware/tools/setup/ubuntu.sh安装需要的依赖,找一个好一点的网络一次成功,有配置不成功的地方之后再说。

然后这其中可能出现的问题就是交叉编译器有问题,推荐2017-q4版本的编译器,gcc-arm-none-eabi-7-2017-q4-major,,这里有链接:

1.https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads2.tar -jxf gcc-arm-none-eabi-7-2017-q4-major-linux.tar.bz2//解压3.gedit .bashrc#在最后一行加入下列代码。注意:gcc-arm-none-eabi-7-2017-q4-major替换成你的4.export PATH=$HOME/gcc-arm-none-eabi-7-2017-q4-major/bin:$PATH#查看版本是否成功5.echo $PATH6.arm-none-eabi-gcc --version

用ROS(melodic)的话建议python2,这样不会冲突。

3.这两个关键步骤做完之后,直接进firmware目录编译固件,然后就会报错,这个时候缺啥补啥就完事啦!

编译make px4_fmu-v5_default下载make px4_fmu-v5_default upload

三、mavros安装(这里推荐使用源码方式安装,因为后续要是修改mavros源码包的话方便修改)

1.我把mavros工作空间放在了home下,打开终端,执行下面命令,会逐级新建文件夹

mkdir -p ~/catkin_ws/srccd ~/catkin_wscatkin initwstool init src

2.安装必要的工具链,加-y目的是为了省略之后要一直输入y来确认安装的步骤

sudo apt-get install python-catkin-tools python-rosinstall-generator -y

3初始化工作空间

wstool init ~/catkin_ws/src

4.安装mavlink,4  5步骤进到src里边执行

rosinstall_generator --rosdistro melodic mavlink | tee /tmp/mavros.rosinstall

这时候可能会出现(这里引用了别人的图片)

这样的意思就是告诉你新的版本是多少,并没有完成安装。我们可以先忽略这个进行下一步。

5.安装mavros

rosinstall_generator --upstream-development mavros | tee -a /tmp/mavros.rosinstall

 如果下载不成功。可以进行换镜像站操作,打开src目录下的.rosinstall文件,将github.com更换为hub.fastgit.org,然后重新执行命令

6.创建工作空间以及依赖,注意回退到上级目录执行,即catkin_ws目录下

wstool merge -t src /tmp/mavros.rosinstallwstool update -t src -j4

rosdep install --from-paths src --ignore-src -y

注意:这里有个大坑,在进行上面两步之后如果直接执行rosdep install这行代码,很大程度上是运行不成功的,因为rosdep并没有进行更新,要执行rosdep update,但是在国内的话rosdep update基本是运行不成功的,因为rosdep update的网址在国外,这里有如下解决办法

找到这几个文件:为了兼容melodic版本,我用的是python2.7,所以这三个文件在这个路径下,每个人情况不同,具体路径视情况而定,找到之后分别按以下的代码进行替换,如果有版本不一样的,可以对照进行修改,主要是在网址前加了代理

sudo gedit /usr/lib/python2.7/dist-packages/rosdep2/rep3.pysudo gedit /usr/lib/python2.7/dist-packages/rosdep2/sources_list.pysudo gedit /usr/lib/python2.7/dist-packages/rosdep2/gbpdistro_suppor

# Copyright (c) 2012, Willow Garage, Inc.# All rights reserved.## Redistribution and use in source and binary forms, with or without# modification, are permitted provided that the following conditions are met:## * Redistributions of source code must retain the above copyright# notice, this list of conditions and the following disclaimer.# * Redistributions in binary form must reproduce the above copyright# notice, this list of conditions and the following disclaimer in the# documentation and/or other materials provided with the distribution.# * Neither the name of the Willow Garage, Inc、nor the names of its# contributors may be used to endorse or promote products derived from# this software without specific prior written permission.## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND ConTRIBUTORS "AS IS"# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE# ARE DISCLAIMED、IN NO EVENT SHALL THE COPYRIGHT OWNER OR ConTRIBUTORS BE# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR# ConSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE# POSSIBILITY OF SUCH DAMAGE.try: from urllib.request import urlopenexcept importError: from urllib2 import urlopenimport yamlimport warningsfrom .core import DownloadFailurefrom .rosdistrohelper import PreRep137Warning# location of targets file for processing gbpdistro filesREP3_TARGETS_URL = 'https://ghproxy.com/https://raw.githubusercontent.com/ros/rosdistro/master/releases/targets.yaml'# seconds to wait before aborting download of gbpdistro dataDOWNLOAD_TIMEOUT = 15.0def download_targets_data(targets_url=None): """ Download REP 3 targets file and unmarshal from YAML. DEPRECATED: this function is deprecated、List of targets should be obtained from the rosdistro module. The body of this function is an example. :param target_url: override URL of platform targets file、Defaults to ``REP3_TARGETS_URL``. :raises: :exc:`DownloadFailure` :raises: :exc:`InvalidData` If targets file does not pass cursory validation checks. """ warnings.warn('deprecated, use rosdistro instead', PreRep137Warning) if targets_url is None: targets_url = REP3_TARGETS_URL try: f = urlopen(targets_url, timeout=DOWNLOAD_TIMEOUT) text = f.read() f.close() targets_data = yaml.safe_load(text) except Exception as e: raise DownloadFailure('Failed to download target platform data for gbpdistro:nt%s' % (str(e))) if type(targets_data) == list: # convert to dictionary new_targets_data = {} for t in targets_data: platform = list(t.keys())[0] new_targets_data[platform] = t[platform] targets_data = new_targets_data return targets_data

# Copyright (c) 2012, Willow Garage, Inc.# All rights reserved.## Redistribution and use in source and binary forms, with or without# modification, are permitted provided that the following conditions are met:## * Redistributions of source code must retain the above copyright# notice, this list of conditions and the following disclaimer.# * Redistributions in binary form must reproduce the above copyright# notice, this list of conditions and the following disclaimer in the# documentation and/or other materials provided with the distribution.# * Neither the name of the Willow Garage, Inc、nor the names of its# contributors may be used to endorse or promote products derived from# this software without specific prior written permission.## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND ConTRIBUTORS "AS IS"# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE# ARE DISCLAIMED、IN NO EVENT SHALL THE COPYRIGHT OWNER OR ConTRIBUTORS BE# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR# ConSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE# POSSIBILITY OF SUCH DAMAGE.# Author Ken Conley/kwc@willowgarage.comfrom __future__ import print_functionimport osimport sysimport yamltry: from urllib.request import urlopen from urllib.error import URLError import urllib.request as requestexcept importError: from urllib2 import urlopen from urllib2 import URLError import urllib2 as requesttry: import cPickle as pickleexcept importError: import picklefrom .cache_tools import compute_filename_hash, PICKLE_CACHE_EXT, write_atomic, write_cache_filefrom .core import InvalidData, DownloadFailure, CachePermissionErrorfrom .gbpdistro_support import get_gbprepo_as_rosdep_data, download_gbpdistro_as_rosdep_datafrom .meta import metaDatabasefrom ._version import __version__try: import urlparseexcept importError: import urllib.parse as urlparse # py3ktry: import httplibexcept importError: import http.client as httplib # py3kimport rospkgimport rospkg.distrofrom .loader import RosdepLoaderfrom .rosdistrohelper import get_index, get_index_url# default file to download with 'init' command in order to bootstrap# rosdepDEFAULT_SOURCES_LIST_URL = 'https://ghproxy.com/https://raw.githubusercontent.com/ros/rosdistro/master/rosdep/sources.list.d/20-default.list'# seconds to wait before aborting download of rosdep dataDOWNLOAD_TIMEOUT = 15.0SOURCES_LIST_DIR = 'sources.list.d'SOURCES_CACHE_DIR = 'sources.cache'# name of index file for sources cacheCACHE_INDEX = 'index'# extension for binary cacheSOURCE_PATH_ENV = 'ROSDEP_SOURCE_PATH'def get_sources_list_dirs(source_list_dir): if SOURCE_PATH_ENV in os.environ: sdirs = os.environ[SOURCE_PATH_ENV].split(os.pathsep) else: sdirs = [source_list_dir] for p in list(sdirs): if not os.path.exists(p): sdirs.remove(p) return sdirsdef get_sources_list_dir(): # base of where we read config files from # TODO: windows if 0: # we can't use etc/ros because environment config does not carry over under sudo etc_ros = rospkg.get_etc_ros_dir() else: etc_ros = '/etc/ros' # compute default system wide sources directory sys_sources_list_dir = os.path.join(etc_ros, 'rosdep', SOURCES_LIST_DIR) sources_list_dirs = get_sources_list_dirs(sys_sources_list_dir) if sources_list_dirs: return sources_list_dirs[0] else: return sys_sources_list_dirdef get_default_sources_list_file(): return os.path.join(get_sources_list_dir(), '20-default.list')def get_sources_cache_dir(): ros_home = rospkg.get_ros_home() return os.path.join(ros_home, 'rosdep', SOURCES_CACHE_DIR)# Default rosdep.yaml format、 For now this is the only valid type and# is specified for future compatibility.TYPE_YAML = 'yaml'# git-buildpackage repo listTYPE_GBPDISTRO = 'gbpdistro'VALID_TYPES = [TYPE_YAML, TYPE_GBPDISTRO]class DataSource(object): def __init__(self, type_, url, tags, origin=None): """ :param type_: data source type, e.g、TYPE_YAML, TYPE_GBPDISTRO :param url: URL of data location、 For file resources, must start with the file:// scheme、 For remote resources, URL must include a path. :param tags: tags for matching data source to configurations :param origin: filename or other indicator of where data came from for debugging. :raises: :exc:`ValueError` if parameters do not validate """ # validate inputs if type_ not in VALID_TYPES: raise ValueError('type must be one of [%s]' % (','.join(VALID_TYPES))) parsed = urlparse.urlparse(url) if not parsed.scheme or (parsed.scheme != 'file' and not parsed.netloc) or parsed.path in ('', '/'): raise ValueError('url must be a fully-specified URL with scheme, hostname, and path: %s' % (str(url))) if not type(tags) == list: raise ValueError('tags must be a list: %s' % (str(tags))) self.type = type_ self.tags = tags self.url = url self.origin = origin def __eq__(self, other): return isinstance(other, DataSource) and self.type == other.type and self.tags == other.tags and self.url == other.url and self.origin == other.origin def __str__(self): if self.origin: return '[%s]:n%s %s %s' % (self.origin, self.type, self.url, ' '.join(self.tags)) else: return '%s %s %s' % (self.type, self.url, ' '.join(self.tags)) def __repr__(self): return repr((self.type, self.url, self.tags, self.origin))class RosDistroSource(DataSource): def __init__(self, distro): self.type = TYPE_GBPDISTRO self.tags = [distro] # In this case self.url is a list if REP-143 is being used self.url = get_index().distributions[distro]['distribution'] self.origin = None# create function we can pass in as model to parse_source_data、 The# function emulates the CachedDataSource constructor but does the# necessary full filepath calculation and loading of data.def cache_data_source_loader(sources_cache_dir, verbose=False): def create_model(type_, uri, tags, origin=None): # compute the filename has from the URL filename = compute_filename_hash(uri) filepath = os.path.join(sources_cache_dir, filename) pickle_filepath = filepath + PICKLE_CACHE_EXT if os.path.exists(pickle_filepath): if verbose: print('loading cached data source:nt%snt%s' % (uri, pickle_filepath), file=sys.stderr) with open(pickle_filepath, 'rb') as f: rosdep_data = pickle.loads(f.read()) elif os.path.exists(filepath): if verbose: print('loading cached data source:nt%snt%s' % (uri, filepath), file=sys.stderr) with open(filepath) as f: rosdep_data = yaml.safe_load(f.read()) else: rosdep_data = {} return CachedDataSource(type_, uri, tags, rosdep_data, origin=filepath) return create_modelclass CachedDataSource(object): def __init__(self, type_, url, tags, rosdep_data, origin=None): """ Stores data source and loaded rosdep data for that source. NOTE: this is not a subclass of DataSource, though it's API is duck-type compatible with the DataSource API. """ self.source = DataSource(type_, url, tags, origin=origin) self.rosdep_data = rosdep_data def __eq__(self, other): try: return self.source == other.source and self.rosdep_data == other.rosdep_data except AttributeError: return False def __str__(self): return '%sn%s' % (self.source, self.rosdep_data) def __repr__(self): return repr((self.type, self.url, self.tags, self.rosdep_data, self.origin)) @property def type(self): """ :returns: data source type """ return self.source.type @property def url(self): """ :returns: data source URL """ return self.source.url @property def tags(self): """ :returns: data source tags """ return self.source.tags @property def origin(self): """ :returns: data source origin, if set, or ``None`` """ return self.source.originclass DataSourceMatcher(object): def __init__(self, tags): self.tags = tags def matches(self, rosdep_data_source): """ Check if the datasource matches this configuration. :param rosdep_data_source: :class:`DataSource` """ # all of the rosdep_data_source tags must be in our matcher tags return not any(set(rosdep_data_source.tags) - set(self.tags)) @staticmethod def create_default(os_override=None): """ Create a :class:`DataSourceMatcher` to match the current configuration. :param os_override: (os_name, os_codename) tuple to override OS detection :returns: :class:`DataSourceMatcher` """ distro_name = rospkg.distro.current_distro_codename() if os_override is None: os_detect = rospkg.os_detect.OsDetect() os_name, os_version, os_codename = os_detect.detect_os() else: os_name, os_codename = os_override tags = [t for t in (distro_name, os_name, os_codename) if t] return DataSourceMatcher(tags)def download_rosdep_data(url): """ :raises: :exc:`DownloadFailure` If data cannot be retrieved (e.g、404, bad YAML format, server down). """ try: # http/https URLs need custom requests to specify the user-agent, since some repositories reject # requests from the default user-agent. url="https://ghproxy.com/"+url if url.startswith("http://") or url.startswith("https://"): url_request = request.Request(url, headers={'User-Agent': 'rosdep/{version}'.format(version=__version__)}) else: url_request = url f = urlopen(url_request, timeout=DOWNLOAD_TIMEOUT) text = f.read() f.close() data = yaml.safe_load(text) if type(data) != dict: raise DownloadFailure('rosdep data from [%s] is not a YAML dictionary' % (url)) return data except (URLError, httplib.HTTPException) as e: raise DownloadFailure(str(e) + ' (%s)' % url) except yaml.YAMLError as e: raise DownloadFailure(str(e))def download_default_sources_list(url=DEFAULT_SOURCES_LIST_URL): """ Download (and validate) contents of default sources list. :param url: override URL of default sources list file :return: raw sources list data, ``str`` :raises: :exc:`DownloadFailure` If data cannot be retrieved (e.g、404, bad YAML format, server down). :raises: :exc:`urllib2.URLError` If data cannot be retrieved (e.g、404, server down). """ try: f = urlopen(url, timeout=DOWNLOAD_TIMEOUT) except (URLError, httplib.HTTPException) as e: raise URLError(str(e) + ' (%s)' % url) data = f.read().decode() f.close() if not data: raise DownloadFailure('cannot download defaults file from %s : empty contents' % url) # parse just for validation try: parse_sources_data(data) except InvalidData as e: raise DownloadFailure( 'The content downloaded from %s failed to pass validation.' ' It is likely that the source is invalid unless the data was corrupted during the download.' ' The contents were:{{{%s}}} The error raised was: %s' % (url, data, e)) return datadef parse_sources_data(data, origin='', model=None): """ Parse sources file format (tags optional):: # comments and empty lines allowed [tags] e.g.:: yaml http://foo/rosdep.yaml fuerte lucid ubuntu If tags are specified, *all* tags must match the current configuration for the sources data to be used. :param data: data in sources file format :param model: model to load data into、 Defaults to :class:`DataSource` :returns: List of data sources, [:class:`DataSource`] :raises: :exc:`InvalidData` """ if model is None: model = DataSource sources = [] for line in data.split('n'): line = line.strip() # ignore empty lines or comments if not line or line.startswith('#'): continue splits = line.split(' ') if len(splits) < 2: raise InvalidData('invalid line:n%s' % (line), origin=origin) type_ = splits[0] url = splits[1] tags = splits[2:] try: sources.append(model(type_, url, tags, origin=origin)) except ValueError as e: raise InvalidData('line:nt%sn%s' % (line, e), origin=origin) return sourcesdef parse_sources_file(filepath): """ Parse file on disk :returns: List of data sources, [:class:`DataSource`] :raises: :exc:`InvalidData` If any error occurs reading file, so an I/O error, non-existent file, or invalid format. """ try: with open(filepath, 'r') as f: return parse_sources_data(f.read(), origin=filepath) except IOError as e: raise InvalidData('I/O error reading sources file: %s' % (str(e)), origin=filepath)def parse_sources_list(sources_list_dir=None): """ Parse data stored in on-disk sources list directory into a list of :class:`DataSource` for processing. :returns: List of data sources, [:class:`DataSource`]、If there is no sources list dir, this returns an empty list. :raises: :exc:`InvalidData` :raises: :exc:`OSError` if *sources_list_dir* cannot be read. :raises: :exc:`IOError` if *sources_list_dir* cannot be read. """ if sources_list_dir is None: sources_list_dir = get_sources_list_dir() sources_list_dirs = get_sources_list_dirs(sources_list_dir) filelist = [] for sdir in sources_list_dirs: filelist += sorted([os.path.join(sdir, f) for f in os.listdir(sdir) if f.endswith('.list')]) sources_list = [] for f in filelist: sources_list.extend(parse_sources_file(f)) return sources_listdef _generate_key_from_urls(urls): # urls may be a list of urls or a single string try: assert isinstance(urls, (list, basestring)) except NameError: assert isinstance(urls, (list, str)) # We join the urls by the '^' character because it is not allowed in urls return '^'.join(urls if isinstance(urls, list) else [urls])def update_sources_list(sources_list_dir=None, sources_cache_dir=None, success_handler=None, error_handler=None, skip_eol_distros=False, ros_distro=None): """ Re-downloaded data from remote sources and store in cache、 Also update the cache index based on current sources. :param sources_list_dir: override source list directory :param sources_cache_dir: override sources cache directory :param success_handler: fn(DataSource) to call if a particular source loads successfully、 This hook is mainly for printing errors to console. :param error_handler: fn(DataSource, DownloadFailure) to call if a particular source fails、 This hook is mainly for printing errors to console. :param skip_eol_distros: skip downloading sources for EOL distros :returns: list of (`DataSource`, cache_file_path) pairs for cache files that were updated, ``[str]`` :raises: :exc:`InvalidData` If any of the sources list files is invalid :raises: :exc:`OSError` if *sources_list_dir* cannot be read. :raises: :exc:`IOError` If *sources_list_dir* cannot be read or cache data cannot be written """ if sources_cache_dir is None: sources_cache_dir = get_sources_cache_dir() sources = parse_sources_list(sources_list_dir=sources_list_dir) retval = [] for source in list(sources): try: if source.type == TYPE_YAML: rosdep_data = download_rosdep_data(source.url) elif source.type == TYPE_GBPDISTRO: # DEPRECATED, do not use this file、See REP137 if not source.tags[0] in ['electric', 'fuerte']: print('Ignore legacy gbpdistro "%s"' % source.tags[0]) sources.remove(source) continue # do not store this entry in the cache rosdep_data = download_gbpdistro_as_rosdep_data(source.url) retval.append((source, write_cache_file(sources_cache_dir, source.url, rosdep_data))) if success_handler is not None: success_handler(source) except DownloadFailure as e: if error_handler is not None: error_handler(source, e) # Additional sources for ros distros # In compliance with REP137 and REP143 python_versions = {} print('Query rosdistro index %s' % get_index_url()) distribution_names = get_index().distributions.keys() if ros_distro is not None and ros_distro not in distribution_names: raise ValueError( 'Requested distribution "%s" is not in the index.' % ros_distro) for dist_name in sorted(distribution_names): distribution = get_index().distributions[dist_name] if dist_name != ros_distro: if ros_distro is not None: print('Skip distro "%s" different from requested "%s"' % (dist_name, ros_distro)) continue if skip_eol_distros: if distribution.get('distribution_status') == 'end-of-life': print('Skip end-of-life distro "%s"' % dist_name) continue print('Add distro "%s"' % dist_name) rds = RosDistroSource(dist_name) rosdep_data = get_gbprepo_as_rosdep_data(dist_name) # Store Python version from REP153 if distribution.get('python_version'): python_versions[dist_name] = distribution.get('python_version') # dist_files can either be a string (single filename) or a list (list of filenames) dist_files = distribution['distribution'] key = _generate_key_from_urls(dist_files) retval.append((rds, write_cache_file(sources_cache_dir, key, rosdep_data))) sources.append(rds) # cache metadata that isn't a source list metaDatabase().set('ROS_PYTHON_VERSION', python_versions) # Create a combined index of *all* the sources、 We do all the # sources regardless of failures because a cache from a previous # attempt may still exist、 We have to do this cache index so that # loads() see consistent data. if not os.path.exists(sources_cache_dir): os.makedirs(sources_cache_dir) cache_index = os.path.join(sources_cache_dir, CACHE_INDEX) data = "#autogenerated by rosdep, do not edit、use 'rosdep update' insteadn" for source in sources: url = _generate_key_from_urls(source.url) data += 'yaml %s %sn' % (url, ' '.join(source.tags)) write_atomic(cache_index, data) # mainly for debugging and testing return retvaldef load_cached_sources_list(sources_cache_dir=None, verbose=False): """ Load cached data based on the sources list. :returns: list of :class:`CachedDataSource` instance with raw rosdep data loaded. :raises: :exc:`OSError` if cache cannot be read :raises: :exc:`IOError` if cache cannot be read """ if sources_cache_dir is None: sources_cache_dir = get_sources_cache_dir() cache_index = os.path.join(sources_cache_dir, 'index') if not os.path.exists(cache_index): if verbose: print('no cache index present, not loading cached sources', file=sys.stderr) return [] try: with open(cache_index, 'r') as f: cache_data = f.read() except IOError as e: if e.strerror == 'Permission denied': raise CachePermissionError('Failed to write cache file: ' + str(e)) else: raise # the loader does all the work model = cache_data_source_loader(sources_cache_dir, verbose=verbose) return parse_sources_data(cache_data, origin=cache_index, model=model)class SourcesListLoader(RosdepLoader): """ SourcesList loader implements the general RosdepLoader API、 This implementation is fairly simple as there is only one view the source list loader can create、 It is also a bit degenerate as it is not capable of mapping resource names to views, thus any resource-name-based API fails or returns nothing interesting. This loader should not be used directly; instead, it is more useful composed with other higher-level implementations, like the :class:`rosdep2.rospkg_loader.RospkgLoader`、 The general intent is to compose it with another loader by making all of the other loader's views depends on all the views in this loader. """ ALL_VIEW_KEY = 'sources.list' def __init__(self, sources): """ :param sources: cached sources list entries, [:class:`CachedDataSource`] """ self.sources = sources @staticmethod def create_default(matcher=None, sources_cache_dir=None, os_override=None, verbose=False): """ :param matcher: override DataSourceMatcher、 Defaults to DataSourceMatcher.create_default(). :param sources_cache_dir: override location of sources cache """ if matcher is None: matcher = DataSourceMatcher.create_default(os_override=os_override) if verbose: print('using matcher with tags [%s]' % (', '.join(matcher.tags)), file=sys.stderr) sources = load_cached_sources_list(sources_cache_dir=sources_cache_dir, verbose=verbose) if verbose: print('loaded %s sources' % (len(sources)), file=sys.stderr) sources = [x for x in sources if matcher.matches(x)] if verbose: print('%s sources match current tags' % (len(sources)), file=sys.stderr) return SourcesListLoader(sources) def load_view(self, view_name, rosdep_db, verbose=False): """ Load view data into rosdep_db、If the view has already been loaded into rosdep_db, this method does nothing. :param view_name: name of ROS stack to load, ``str`` :param rosdep_db: database to load stack data into, :class:`RosdepDatabase` :raises: :exc:`InvalidData` """ if rosdep_db.is_loaded(view_name): return source = self.get_source(view_name) if verbose: print('loading view [%s] with sources.list loader' % (view_name), file=sys.stderr) view_dependencies = self.get_view_dependencies(view_name) rosdep_db.set_view_data(view_name, source.rosdep_data, view_dependencies, view_name) def get_loadable_resources(self): return [] def get_loadable_views(self): return [x.url for x in self.sources] def get_view_dependencies(self, view_name): # use dependencies to implement precedence if view_name != SourcesListLoader.ALL_VIEW_KEY: # if the view_name matches one of our sources, return # empty list as none of our sources has deps. if any([x for x in self.sources if view_name == x.url]): return [] # not one of our views, so it depends on everything we provide return [x.url for x in self.sources] def get_source(self, view_name): matches = [x for x in self.sources if x.url == view_name] if matches: return matches[0] else: raise rospkg.ResourceNotFound(view_name) def get_rosdeps(self, resource_name, implicit=True): """ Always raises as SourceListLoader defines no concrete resources with rosdeps. :raises: :exc:`rospkg.ResourceNotFound` """ raise rospkg.ResourceNotFound(resource_name) def get_view_key(self, resource_name): """ Always raises as SourceListLoader defines no concrete resources with rosdeps. :returns: Name of view that *resource_name* is in, ``None`` if no associated view. :raises: :exc:`rospkg.ResourceNotFound` if *resource_name* cannot be found. """ raise rospkg.ResourceNotFound(resource_name)

# Copyright (c) 2012, Willow Garage, Inc.# All rights reserved.## Redistribution and use in source and binary forms, with or without# modification, are permitted provided that the following conditions are met:## * Redistributions of source code must retain the above copyright# notice, this list of conditions and the following disclaimer.# * Redistributions in binary form must reproduce the above copyright# notice, this list of conditions and the following disclaimer in the# documentation and/or other materials provided with the distribution.# * Neither the name of the Willow Garage, Inc、nor the names of its# contributors may be used to endorse or promote products derived from# this software without specific prior written permission.## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND ConTRIBUTORS "AS IS"# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE# ARE DISCLAIMED、IN NO EVENT SHALL THE COPYRIGHT OWNER OR ConTRIBUTORS BE# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR# ConSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE# POSSIBILITY OF SUCH DAMAGE.# Author Ken Conley/kwc@willowgarage.comfrom __future__ import print_functionimport osimport sysimport yamltry: from urllib.request import urlopen from urllib.error import URLError import urllib.request as requestexcept importError: from urllib2 import urlopen from urllib2 import URLError import urllib2 as requesttry: import cPickle as pickleexcept importError: import picklefrom .cache_tools import compute_filename_hash, PICKLE_CACHE_EXT, write_atomic, write_cache_filefrom .core import InvalidData, DownloadFailure, CachePermissionErrorfrom .gbpdistro_support import get_gbprepo_as_rosdep_data, download_gbpdistro_as_rosdep_datafrom .meta import metaDatabasefrom ._version import __version__try: import urlparseexcept importError: import urllib.parse as urlparse # py3ktry: import httplibexcept importError: import http.client as httplib # py3kimport rospkgimport rospkg.distrofrom .loader import RosdepLoaderfrom .rosdistrohelper import get_index, get_index_url# default file to download with 'init' command in order to bootstrap# rosdepDEFAULT_SOURCES_LIST_URL = 'https://ghproxy.com/https://raw.githubusercontent.com/ros/rosdistro/master/rosdep/sources.list.d/20-default.list'# seconds to wait before aborting download of rosdep dataDOWNLOAD_TIMEOUT = 15.0SOURCES_LIST_DIR = 'sources.list.d'SOURCES_CACHE_DIR = 'sources.cache'# name of index file for sources cacheCACHE_INDEX = 'index'# extension for binary cacheSOURCE_PATH_ENV = 'ROSDEP_SOURCE_PATH'def get_sources_list_dirs(source_list_dir): if SOURCE_PATH_ENV in os.environ: sdirs = os.environ[SOURCE_PATH_ENV].split(os.pathsep) else: sdirs = [source_list_dir] for p in list(sdirs): if not os.path.exists(p): sdirs.remove(p) return sdirsdef get_sources_list_dir(): # base of where we read config files from # TODO: windows if 0: # we can't use etc/ros because environment config does not carry over under sudo etc_ros = rospkg.get_etc_ros_dir() else: etc_ros = '/etc/ros' # compute default system wide sources directory sys_sources_list_dir = os.path.join(etc_ros, 'rosdep', SOURCES_LIST_DIR) sources_list_dirs = get_sources_list_dirs(sys_sources_list_dir) if sources_list_dirs: return sources_list_dirs[0] else: return sys_sources_list_dirdef get_default_sources_list_file(): return os.path.join(get_sources_list_dir(), '20-default.list')def get_sources_cache_dir(): ros_home = rospkg.get_ros_home() return os.path.join(ros_home, 'rosdep', SOURCES_CACHE_DIR)# Default rosdep.yaml format、 For now this is the only valid type and# is specified for future compatibility.TYPE_YAML = 'yaml'# git-buildpackage repo listTYPE_GBPDISTRO = 'gbpdistro'VALID_TYPES = [TYPE_YAML, TYPE_GBPDISTRO]class DataSource(object): def __init__(self, type_, url, tags, origin=None): """ :param type_: data source type, e.g、TYPE_YAML, TYPE_GBPDISTRO :param url: URL of data location、 For file resources, must start with the file:// scheme、 For remote resources, URL must include a path. :param tags: tags for matching data source to configurations :param origin: filename or other indicator of where data came from for debugging. :raises: :exc:`ValueError` if parameters do not validate """ # validate inputs if type_ not in VALID_TYPES: raise ValueError('type must be one of [%s]' % (','.join(VALID_TYPES))) parsed = urlparse.urlparse(url) if not parsed.scheme or (parsed.scheme != 'file' and not parsed.netloc) or parsed.path in ('', '/'): raise ValueError('url must be a fully-specified URL with scheme, hostname, and path: %s' % (str(url))) if not type(tags) == list: raise ValueError('tags must be a list: %s' % (str(tags))) self.type = type_ self.tags = tags self.url = url self.origin = origin def __eq__(self, other): return isinstance(other, DataSource) and self.type == other.type and self.tags == other.tags and self.url == other.url and self.origin == other.origin def __str__(self): if self.origin: return '[%s]:n%s %s %s' % (self.origin, self.type, self.url, ' '.join(self.tags)) else: return '%s %s %s' % (self.type, self.url, ' '.join(self.tags)) def __repr__(self): return repr((self.type, self.url, self.tags, self.origin))class RosDistroSource(DataSource): def __init__(self, distro): self.type = TYPE_GBPDISTRO self.tags = [distro] # In this case self.url is a list if REP-143 is being used self.url = get_index().distributions[distro]['distribution'] self.origin = None# create function we can pass in as model to parse_source_data、 The# function emulates the CachedDataSource constructor but does the# necessary full filepath calculation and loading of data.def cache_data_source_loader(sources_cache_dir, verbose=False): def create_model(type_, uri, tags, origin=None): # compute the filename has from the URL filename = compute_filename_hash(uri) filepath = os.path.join(sources_cache_dir, filename) pickle_filepath = filepath + PICKLE_CACHE_EXT if os.path.exists(pickle_filepath): if verbose: print('loading cached data source:nt%snt%s' % (uri, pickle_filepath), file=sys.stderr) with open(pickle_filepath, 'rb') as f: rosdep_data = pickle.loads(f.read()) elif os.path.exists(filepath): if verbose: print('loading cached data source:nt%snt%s' % (uri, filepath), file=sys.stderr) with open(filepath) as f: rosdep_data = yaml.safe_load(f.read()) else: rosdep_data = {} return CachedDataSource(type_, uri, tags, rosdep_data, origin=filepath) return create_modelclass CachedDataSource(object): def __init__(self, type_, url, tags, rosdep_data, origin=None): """ Stores data source and loaded rosdep data for that source. NOTE: this is not a subclass of DataSource, though it's API is duck-type compatible with the DataSource API. """ self.source = DataSource(type_, url, tags, origin=origin) self.rosdep_data = rosdep_data def __eq__(self, other): try: return self.source == other.source and self.rosdep_data == other.rosdep_data except AttributeError: return False def __str__(self): return '%sn%s' % (self.source, self.rosdep_data) def __repr__(self): return repr((self.type, self.url, self.tags, self.rosdep_data, self.origin)) @property def type(self): """ :returns: data source type """ return self.source.type @property def url(self): """ :returns: data source URL """ return self.source.url @property def tags(self): """ :returns: data source tags """ return self.source.tags @property def origin(self): """ :returns: data source origin, if set, or ``None`` """ return self.source.originclass DataSourceMatcher(object): def __init__(self, tags): self.tags = tags def matches(self, rosdep_data_source): """ Check if the datasource matches this configuration. :param rosdep_data_source: :class:`DataSource` """ # all of the rosdep_data_source tags must be in our matcher tags return not any(set(rosdep_data_source.tags) - set(self.tags)) @staticmethod def create_default(os_override=None): """ Create a :class:`DataSourceMatcher` to match the current configuration. :param os_override: (os_name, os_codename) tuple to override OS detection :returns: :class:`DataSourceMatcher` """ distro_name = rospkg.distro.current_distro_codename() if os_override is None: os_detect = rospkg.os_detect.OsDetect() os_name, os_version, os_codename = os_detect.detect_os() else: os_name, os_codename = os_override tags = [t for t in (distro_name, os_name, os_codename) if t] return DataSourceMatcher(tags)def download_rosdep_data(url): """ :raises: :exc:`DownloadFailure` If data cannot be retrieved (e.g、404, bad YAML format, server down). """ try: # http/https URLs need custom requests to specify the user-agent, since some repositories reject # requests from the default user-agent. url="https://ghproxy.com/"+url if url.startswith("http://") or url.startswith("https://"): url_request = request.Request(url, headers={'User-Agent': 'rosdep/{version}'.format(version=__version__)}) else: url_request = url f = urlopen(url_request, timeout=DOWNLOAD_TIMEOUT) text = f.read() f.close() data = yaml.safe_load(text) if type(data) != dict: raise DownloadFailure('rosdep data from [%s] is not a YAML dictionary' % (url)) return data except (URLError, httplib.HTTPException) as e: raise DownloadFailure(str(e) + ' (%s)' % url) except yaml.YAMLError as e: raise DownloadFailure(str(e))def download_default_sources_list(url=DEFAULT_SOURCES_LIST_URL): """ Download (and validate) contents of default sources list. :param url: override URL of default sources list file :return: raw sources list data, ``str`` :raises: :exc:`DownloadFailure` If data cannot be retrieved (e.g、404, bad YAML format, server down). :raises: :exc:`urllib2.URLError` If data cannot be retrieved (e.g、404, server down). """ try: f = urlopen(url, timeout=DOWNLOAD_TIMEOUT) except (URLError, httplib.HTTPException) as e: raise URLError(str(e) + ' (%s)' % url) data = f.read().decode() f.close() if not data: raise DownloadFailure('cannot download defaults file from %s : empty contents' % url) # parse just for validation try: parse_sources_data(data) except InvalidData as e: raise DownloadFailure( 'The content downloaded from %s failed to pass validation.' ' It is likely that the source is invalid unless the data was corrupted during the download.' ' The contents were:{{{%s}}} The error raised was: %s' % (url, data, e)) return datadef parse_sources_data(data, origin='', model=None): """ Parse sources file format (tags optional):: # comments and empty lines allowed [tags] e.g.:: yaml http://foo/rosdep.yaml fuerte lucid ubuntu If tags are specified, *all* tags must match the current configuration for the sources data to be used. :param data: data in sources file format :param model: model to load data into、 Defaults to :class:`DataSource` :returns: List of data sources, [:class:`DataSource`] :raises: :exc:`InvalidData` """ if model is None: model = DataSource sources = [] for line in data.split('n'): line = line.strip() # ignore empty lines or comments if not line or line.startswith('#'): continue splits = line.split(' ') if len(splits) < 2: raise InvalidData('invalid line:n%s' % (line), origin=origin) type_ = splits[0] url = splits[1] tags = splits[2:] try: sources.append(model(type_, url, tags, origin=origin)) except ValueError as e: raise InvalidData('line:nt%sn%s' % (line, e), origin=origin) return sourcesdef parse_sources_file(filepath): """ Parse file on disk :returns: List of data sources, [:class:`DataSource`] :raises: :exc:`InvalidData` If any error occurs reading file, so an I/O error, non-existent file, or invalid format. """ try: with open(filepath, 'r') as f: return parse_sources_data(f.read(), origin=filepath) except IOError as e: raise InvalidData('I/O error reading sources file: %s' % (str(e)), origin=filepath)def parse_sources_list(sources_list_dir=None): """ Parse data stored in on-disk sources list directory into a list of :class:`DataSource` for processing. :returns: List of data sources, [:class:`DataSource`]、If there is no sources list dir, this returns an empty list. :raises: :exc:`InvalidData` :raises: :exc:`OSError` if *sources_list_dir* cannot be read. :raises: :exc:`IOError` if *sources_list_dir* cannot be read. """ if sources_list_dir is None: sources_list_dir = get_sources_list_dir() sources_list_dirs = get_sources_list_dirs(sources_list_dir) filelist = [] for sdir in sources_list_dirs: filelist += sorted([os.path.join(sdir, f) for f in os.listdir(sdir) if f.endswith('.list')]) sources_list = [] for f in filelist: sources_list.extend(parse_sources_file(f)) return sources_listdef _generate_key_from_urls(urls): # urls may be a list of urls or a single string try: assert isinstance(urls, (list, basestring)) except NameError: assert isinstance(urls, (list, str)) # We join the urls by the '^' character because it is not allowed in urls return '^'.join(urls if isinstance(urls, list) else [urls])def update_sources_list(sources_list_dir=None, sources_cache_dir=None, success_handler=None, error_handler=None, skip_eol_distros=False, ros_distro=None): """ Re-downloaded data from remote sources and store in cache、 Also update the cache index based on current sources. :param sources_list_dir: override source list directory :param sources_cache_dir: override sources cache directory :param success_handler: fn(DataSource) to call if a particular source loads successfully、 This hook is mainly for printing errors to console. :param error_handler: fn(DataSource, DownloadFailure) to call if a particular source fails、 This hook is mainly for printing errors to console. :param skip_eol_distros: skip downloading sources for EOL distros :returns: list of (`DataSource`, cache_file_path) pairs for cache files that were updated, ``[str]`` :raises: :exc:`InvalidData` If any of the sources list files is invalid :raises: :exc:`OSError` if *sources_list_dir* cannot be read. :raises: :exc:`IOError` If *sources_list_dir* cannot be read or cache data cannot be written """ if sources_cache_dir is None: sources_cache_dir = get_sources_cache_dir() sources = parse_sources_list(sources_list_dir=sources_list_dir) retval = [] for source in list(sources): try: if source.type == TYPE_YAML: rosdep_data = download_rosdep_data(source.url) elif source.type == TYPE_GBPDISTRO: # DEPRECATED, do not use this file、See REP137 if not source.tags[0] in ['electric', 'fuerte']: print('Ignore legacy gbpdistro "%s"' % source.tags[0]) sources.remove(source) continue # do not store this entry in the cache rosdep_data = download_gbpdistro_as_rosdep_data(source.url) retval.append((source, write_cache_file(sources_cache_dir, source.url, rosdep_data))) if success_handler is not None: success_handler(source) except DownloadFailure as e: if error_handler is not None: error_handler(source, e) # Additional sources for ros distros # In compliance with REP137 and REP143 python_versions = {} print('Query rosdistro index %s' % get_index_url()) distribution_names = get_index().distributions.keys() if ros_distro is not None and ros_distro not in distribution_names: raise ValueError( 'Requested distribution "%s" is not in the index.' % ros_distro) for dist_name in sorted(distribution_names): distribution = get_index().distributions[dist_name] if dist_name != ros_distro: if ros_distro is not None: print('Skip distro "%s" different from requested "%s"' % (dist_name, ros_distro)) continue if skip_eol_distros: if distribution.get('distribution_status') == 'end-of-life': print('Skip end-of-life distro "%s"' % dist_name) continue print('Add distro "%s"' % dist_name) rds = RosDistroSource(dist_name) rosdep_data = get_gbprepo_as_rosdep_data(dist_name) # Store Python version from REP153 if distribution.get('python_version'): python_versions[dist_name] = distribution.get('python_version') # dist_files can either be a string (single filename) or a list (list of filenames) dist_files = distribution['distribution'] key = _generate_key_from_urls(dist_files) retval.append((rds, write_cache_file(sources_cache_dir, key, rosdep_data))) sources.append(rds) # cache metadata that isn't a source list metaDatabase().set('ROS_PYTHON_VERSION', python_versions) # Create a combined index of *all* the sources、 We do all the # sources regardless of failures because a cache from a previous # attempt may still exist、 We have to do this cache index so that # loads() see consistent data. if not os.path.exists(sources_cache_dir): os.makedirs(sources_cache_dir) cache_index = os.path.join(sources_cache_dir, CACHE_INDEX) data = "#autogenerated by rosdep, do not edit、use 'rosdep update' insteadn" for source in sources: url = _generate_key_from_urls(source.url) data += 'yaml %s %sn' % (url, ' '.join(source.tags)) write_atomic(cache_index, data) # mainly for debugging and testing return retvaldef load_cached_sources_list(sources_cache_dir=None, verbose=False): """ Load cached data based on the sources list. :returns: list of :class:`CachedDataSource` instance with raw rosdep data loaded. :raises: :exc:`OSError` if cache cannot be read :raises: :exc:`IOError` if cache cannot be read """ if sources_cache_dir is None: sources_cache_dir = get_sources_cache_dir() cache_index = os.path.join(sources_cache_dir, 'index') if not os.path.exists(cache_index): if verbose: print('no cache index present, not loading cached sources', file=sys.stderr) return [] try: with open(cache_index, 'r') as f: cache_data = f.read() except IOError as e: if e.strerror == 'Permission denied': raise CachePermissionError('Failed to write cache file: ' + str(e)) else: raise # the loader does all the work model = cache_data_source_loader(sources_cache_dir, verbose=verbose) return parse_sources_data(cache_data, origin=cache_index, model=model)class SourcesListLoader(RosdepLoader): """ SourcesList loader implements the general RosdepLoader API、 This implementation is fairly simple as there is only one view the source list loader can create、 It is also a bit degenerate as it is not capable of mapping resource names to views, thus any resource-name-based API fails or returns nothing interesting. This loader should not be used directly; instead, it is more useful composed with other higher-level implementations, like the :class:`rosdep2.rospkg_loader.RospkgLoader`、 The general intent is to compose it with another loader by making all of the other loader's views depends on all the views in this loader. """ ALL_VIEW_KEY = 'sources.list' def __init__(self, sources): """ :param sources: cached sources list entries, [:class:`CachedDataSource`] """ self.sources = sources @staticmethod def create_default(matcher=None, sources_cache_dir=None, os_override=None, verbose=False): """ :param matcher: override DataSourceMatcher、 Defaults to DataSourceMatcher.create_default(). :param sources_cache_dir: override location of sources cache """ if matcher is None: matcher = DataSourceMatcher.create_default(os_override=os_override) if verbose: print('using matcher with tags [%s]' % (', '.join(matcher.tags)), file=sys.stderr) sources = load_cached_sources_list(sources_cache_dir=sources_cache_dir, verbose=verbose) if verbose: print('loaded %s sources' % (len(sources)), file=sys.stderr) sources = [x for x in sources if matcher.matches(x)] if verbose: print('%s sources match current tags' % (len(sources)), file=sys.stderr) return SourcesListLoader(sources) def load_view(self, view_name, rosdep_db, verbose=False): """ Load view data into rosdep_db、If the view has already been loaded into rosdep_db, this method does nothing. :param view_name: name of ROS stack to load, ``str`` :param rosdep_db: database to load stack data into, :class:`RosdepDatabase` :raises: :exc:`InvalidData` """ if rosdep_db.is_loaded(view_name): return source = self.get_source(view_name) if verbose: print('loading view [%s] with sources.list loader' % (view_name), file=sys.stderr) view_dependencies = self.get_view_dependencies(view_name) rosdep_db.set_view_data(view_name, source.rosdep_data, view_dependencies, view_name) def get_loadable_resources(self): return [] def get_loadable_views(self): return [x.url for x in self.sources] def get_view_dependencies(self, view_name): # use dependencies to implement precedence if view_name != SourcesListLoader.ALL_VIEW_KEY: # if the view_name matches one of our sources, return # empty list as none of our sources has deps. if any([x for x in self.sources if view_name == x.url]): return [] # not one of our views, so it depends on everything we provide return [x.url for x in self.sources] def get_source(self, view_name): matches = [x for x in self.sources if x.url == view_name] if matches: return matches[0] else: raise rospkg.ResourceNotFound(view_name) def get_rosdeps(self, resource_name, implicit=True): """ Always raises as SourceListLoader defines no concrete resources with rosdeps. :raises: :exc:`rospkg.ResourceNotFound` """ raise rospkg.ResourceNotFound(resource_name) def get_view_key(self, resource_name): """ Always raises as SourceListLoader defines no concrete resources with rosdeps. :returns: Name of view that *resource_name* is in, ``None`` if no associated view. :raises: :exc:`rospkg.ResourceNotFound` if *resource_name* cannot be found. """ raise rospkg.ResourceNotFound(resource_name)

完成以上步骤之后,mavros环境就基本配置成功啦! 下期再更新offboard控制+gazebo仿真以及与pixhawk4飞控连接!大家加油!

Copyright © 2016-2020 www.365daan.com All Rights Reserved. 365答案网 版权所有 备案号:

部分内容来自互联网,版权归原作者所有,如有冒犯请联系我们,我们将在三个工作时内妥善处理。