最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

在Ubuntu22.04下編譯ros2 humble的Android版本

2023-03-06 16:10 作者:古月居GYH  | 我要投稿

前提

我一直想搗鼓一下機(jī)器人開發(fā),想著自己獨(dú)立去設(shè)計(jì)一款機(jī)器人,但了解到整個(gè)流程中所要學(xué)習(xí)的技能以及自己為數(shù)不多的業(yè)余時(shí)間,這事兒讓我覺得有點(diǎn)犯難。直到去年看到古月居發(fā)布了OriginBot智能小車,在看完它的整體資料后,我覺得它有以下幾個(gè)特點(diǎn)比較吸引我:

  • 開源特性,基本上展現(xiàn)了如何從頭到尾去設(shè)計(jì)開發(fā)一款小型的機(jī)器人,這和我的初始想法非常吻合。

  • 軟硬件的可拓展性,旭日X3派提升了它的可開發(fā)性,畢竟我不希望買它回來只是跑跑例程。

  • 價(jià)格,在同類型的產(chǎn)品中它的價(jià)格比較親民。


在組裝好小車并跑了一些例程后,我覺得小車還缺少一個(gè)遙控器,畢竟不能一直端著筆記本電腦去控制小車吧,但是我又不想買手柄,于是我把目光投向了我身邊的那部沉睡已久的安卓手機(jī),想法來了,那就用手機(jī)去遙控小車吧。

其實(shí)用手機(jī)控制小車這也不是什么新鮮事,網(wǎng)上例子也很多,藍(lán)牙,WiFi都可以,雖然都是學(xué)習(xí),但是我也想整點(diǎn)不一樣的,后來查資料發(fā)現(xiàn)ROS2也有安卓的版本,這就正合我意了,而且有了ROS2也能做更多的事情了。

我其實(shí)并不會(huì)安卓開發(fā),也不會(huì)java,但是安卓開發(fā)作為一門很成熟的技術(shù),咱們程序員稍微學(xué)習(xí)一下,開發(fā)點(diǎn)簡單的應(yīng)用應(yīng)該沒什么問題。問題的關(guān)鍵是如何編譯ROS2的安卓版本,下面就來講講整個(gè)編譯過程以及我踩過的坑吧。

編譯過程

系統(tǒng):Ubuntu22.04

GitHub上的原項(xiàng)目:https://github.com/esteve/ros2_java

編譯的步驟基本和原項(xiàng)目一致,但是這個(gè)項(xiàng)目的版本比較老,有些步驟直接使用會(huì)報(bào)錯(cuò),所以經(jīng)過我踩坑后的步驟如下:

1、配置Android SDK


可以直接下載Android studio進(jìn)行配置,在初次啟動(dòng)Android studio時(shí)會(huì)提示用戶安裝必要的sdk和其他模塊,一般Ubuntu系統(tǒng)會(huì)安裝在用戶目錄下。

2、配置Android NDK


可以直接在Android studio中進(jìn)行下載配置,也可以前往NDK官網(wǎng)下載,下載后可以和SDK放在同一目錄,至于NDK的版本選最新的就可以。

3、設(shè)置環(huán)境變量


在.bashrc文件最后一排設(shè)置SDK和NDK的環(huán)境變量,如下

export?ANDROID_HOEM=~/Android/Sdk
export?PATH=$PATH:$ANDROID_HOEM/tools:$ANDROID_HOEM/platform-tool
export?ANDROID_NDK=~/Android/ndk-r25b

4、克隆ros2和ros2 java源碼

mkdir?-p?$HOME/ros2_android_ws/src
cd?$HOME/ros2_android_ws
curl?https://raw.githubusercontent.com/ros2-java/ros2_java/main/ros2_java_android.repos?|?vcs?import?src

原項(xiàng)目的版本是ros2 galactic,想要換成最新的長期支持版本humble也可以,需要將ros2_java_android.repos里面的galactic字樣修改為humble,其中Fast DDS的版本也可以更新為最新的版本。


我也整理了一份humble的repos:

curl?https://raw.githubusercontent.com/uglymie/ros2-humble-for-android/main/ros2_java_android.repos?|?vcs?import?src

此處要特別注意一個(gè)問題,因?yàn)楸卷?xiàng)目需要大量下載GitHub上的ROS相關(guān)模塊,所以要求電腦終端必須要能流暢的訪問GitHub,強(qiáng)調(diào)一下,這個(gè)是必要條件。

最終得到的文件目錄如下圖:

5、設(shè)置編譯配置

export?PYTHON3_EXEC="$(?which?python3?)"
export?PYTHON3_LIBRARY="$(?${PYTHON3_EXEC}?-c?'import?os.path;?from?distutils?import?sysconfig;?print(os.path.realpath(os.path.join(sysconfig.get_config_var("LIBPL"),?sysconfig.get_config_var("LDLIBRARY"))))'?)"
export?PYTHON3_INCLUDE_DIR="$(?${PYTHON3_EXEC}?-c?'from?distutils?import?sysconfig;?print(sysconfig.get_config_var("INCLUDEPY"))'?)"
export?ANDROID_ABI=armeabi-v7a
export?ANDROID_NATIVE_API_LEVEL=android-29
export?ANDROID_TOOLCHAIN_NAME=arm-linux-androideabi-clang

其中ANDROID相關(guān)選項(xiàng)可以更換,如

export?ANDROID_ABI=arm64-v8a
export?ANDROID_NATIVE_API_LEVEL=android-29
export?ANDROID_TOOLCHAIN_NAME=aarch64-linux-android-clang

這里我們選擇 ANDROID_ABI 為arm64-v8a

6、編譯命令

colcon?build?\
???--packages-ignore?cyclonedds?rcl_logging_log4cxx?rcl_logging_spdlog?rosidl_generator_py?rclandroid?ros2_talker_android?ros2_listener_android?\
???--cmake-args?\
???-DPYTHON_EXECUTABLE=${PYTHON3_EXEC}?\
???-DPYTHON_LIBRARY=${PYTHON3_LIBRARY}?\
???-DPYTHON_INCLUDE_DIR=${PYTHON3_INCLUDE_DIR}?\
???-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}?\
???-DANDROID=ON?\
???-DANDROID_FUNCTION_LEVEL_LINKING=OFF?\
???-DANDROID_NATIVE_API_LEVEL=${ANDROID_TARGET}?\
???-DANDROID_TOOLCHAIN_NAME=${ANDROID_TOOLCHAIN_NAME}?\
???-DANDROID_STL=c++_shared?\
???-DANDROID_ABI=${ANDROID_ABI}?\
???-DANDROID_NDK=${ANDROID_NDK}?\
???-DTHIRDPARTY=ON??\
???-DCOMPILE_EXAMPLES=OFF?\
???-DCMAKE_FIND_ROOT_PATH="${PWD}/install"?\
???-DBUILD_TESTING=OFF?\
???-DRCL_LOGGING_IMPLEMENTATION=rcl_logging_noop?\
???-DTHIRDPARTY_android-ifaddrs=FORCE


此編譯命令經(jīng)過多次問題排查更改,已經(jīng)可以避免大多數(shù)錯(cuò)誤。但是仍然可能出現(xiàn)其他錯(cuò)誤:

找不到j(luò)ni.h和jni_md.h

fatal?error:?jni.h:?No?such?file?or?directory

解決方法: 可以添加全局搜索路徑到 .bashrc 或者配置文件 /etc/profile 中,其中java-11-openjdk-amd64為當(dāng)前系統(tǒng)已經(jīng)安裝的jdk版本,CPATH關(guān)鍵字表示適用于所有語言。

export?CPATH=/usr/lib/jvm/java-11-openjdk-amd64/include:$CPATH
export?CPATH=/usr/lib/jvm/java-11-openjdk-amd64/include/linux:$CPATH

編譯到Fast-DDS時(shí)可能出現(xiàn)找不到asio和tinyxml2的頭文件

解決方法: 可以在其目錄下(eProsima/Fast-DDS)的CMakeList.txt文件里添加包含頭文件路徑

include_directories(thirdparty/asio/asio/include)
include_directories(thirdparty/tinyxml2)

打包過程

1、jar文件和so文件

編譯完成后在install目錄下會(huì)生成很多文件夾,其中包含了所有的jar文件和so文件,可以直接在目錄下搜索,如下圖:

可以將兩種文件分別拷貝并整理到單獨(dú)的文件目錄下,比如:

ros2-humble/arm64-v8a/jar
ros2-humble/arm64-v8a/so

2.、根據(jù)需要加載庫文件


編譯好的jar文件和so文件包含了比較完整的ros常用功能包,以下是so文件:

可以看到共有1144個(gè)文件,事實(shí)上很多消息類的庫文件我們是按需所用,比如項(xiàng)目中只需要用到std_msgs,那么其他的消息類庫文件就可以不用再加載,如果加載所有的庫文件會(huì)使最終的apk文件變得比較大。

測(cè)試應(yīng)用程序

下面是一個(gè)測(cè)試APP的簡單說明,這個(gè)應(yīng)用也是我之前學(xué)習(xí)的時(shí)候在GitHub上找到的:https://github.com/YasuChiba/ros2-android-test-app


我覺得拿來作為測(cè)試用的應(yīng)用比較合適,當(dāng)然這個(gè)應(yīng)用使用的庫文件是ros2 galactic編譯好的,我將其替換成humble也是沒問題的。

測(cè)試APP主界面包括四個(gè)Button和一個(gè)TextView(空白處),如下圖:

要實(shí)現(xiàn)的功能也比較簡單,建立一個(gè)發(fā)布者和一個(gè)訂閱者,定時(shí)發(fā)布消息,其話題名稱為/chatter,消息類型為字符串,可以開始和暫停發(fā)布或訂閱。
Android相關(guān)代碼這里就不做說明,可以看上面完整的項(xiàng)目;下面看一下在java語言下的ros發(fā)布及訂閱節(jié)點(diǎn)的實(shí)現(xiàn)。

發(fā)布者節(jié)點(diǎn),TalkerNode.java:

package?com.example.ros2_android_test_app;import?java.util.concurrent.TimeUnit;import?android.util.Log;import?org.ros2.rcljava.node.BaseComposableNode;?//?引入ROS2節(jié)點(diǎn)相關(guān)庫import?org.ros2.rcljava.publisher.Publisher;?//?引入發(fā)布者相關(guān)庫import?org.ros2.rcljava.timer.WallTimer;?//?引入計(jì)時(shí)器相關(guān)庫public?class?TalkerNode?extends?BaseComposableNode?{private?static?String?logtag?=?TalkerNode.class.getName();private?final?String?topic;?//?定義節(jié)點(diǎn)發(fā)布的話題public?Publisher<std_msgs.msg.String>?publisher;?//?聲明發(fā)布者private?int?count;?//?定義計(jì)數(shù)器private?WallTimer?timer;?//?聲明計(jì)時(shí)器public?TalkerNode(final?String?name,?final?String?topic)?{
????super(name);
????this.topic?=?topic;
????//?創(chuàng)建發(fā)布者
????this.publisher?=?this.node.<std_msgs.msg.String>createPublisher(
????????????std_msgs.msg.String.class,?this.topic);}public?void?start()?{
????Log.d(logtag,?"TalkerNode::start()");
????if?(this.timer?!=?null)?{
????????this.timer.cancel();?//?如果計(jì)時(shí)器已存在,取消計(jì)時(shí)器
????}
????this.count?=?0;?//?將計(jì)數(shù)器歸零
????//?創(chuàng)建計(jì)時(shí)器,每500毫秒執(zhí)行一次onTimer函數(shù)
????this.timer?=?node.createWallTimer(500,?TimeUnit.MILLISECONDS,?this::onTimer);}private?void?onTimer()?{
????std_msgs.msg.String?msg?=?new?std_msgs.msg.String();?//?創(chuàng)建消息對(duì)象
????msg.setData("Hello!?ROS2?Humble!?"?+?this.count);?//?設(shè)置消息內(nèi)容
????this.count++;?//?計(jì)數(shù)器自增
????this.publisher.publish(msg);?//?發(fā)布消息}public?void?stop()?{
????Log.d(logtag,?"TalkerNode::stop()");
????if?(this.timer?!=?null)?{
????????this.timer.cancel();?//?如果計(jì)時(shí)器已存在,取消計(jì)時(shí)器
????}}


訂閱者節(jié)點(diǎn),ListenerNode.java:

package?com.example.ros2_android_test_app;import?android.util.Log;import?android.widget.TextView;import?org.ros2.rcljava.node.BaseComposableNode;import?org.ros2.rcljava.subscription.Subscription;public?class?ListenerNode?extends?BaseComposableNode?{
????private?final?String?topic;???//?訂閱的?ROS2?消息主題名稱
????private?final?TextView?listenerView;??//?用于在?Android?UI?上顯示消息內(nèi)容的?TextView?控件

????private?Subscription<std_msgs.msg.String>?subscriber;??//?ROS2?訂閱者對(duì)象,用于接收消息

????public?ListenerNode(final?String?name,?final?String?topic,
????????????????????????final?TextView?listenerView)?{
????????super(name);??//?調(diào)用父類?BaseComposableNode?的構(gòu)造方法,傳入節(jié)點(diǎn)名稱
????????this.topic?=?topic;??//?保存訂閱的主題名稱
????????this.listenerView?=?listenerView;??//?保存用于顯示消息內(nèi)容的?TextView?控件

????????//?創(chuàng)建?ROS2?訂閱者對(duì)象,訂閱指定主題的?std_msgs/String?類型的消息
????????this.subscriber?=?this.node.<std_msgs.msg.String>createSubscription(
????????????????std_msgs.msg.String.class,?this.topic,?msg????????????????????????->?{
????????????????????//?當(dāng)接收到新消息時(shí),將其內(nèi)容顯示在?TextView?控件上
????????????????????this.listenerView.setText("Hello?ROS2?from?Android:?"?+?msg.getData()?+
????????????????????????????"\r\n"?+?listenerView.getText());
????????????????});
????}}

用于管理 ROS 的執(zhí)行器(Executor)和在 Android 設(shè)備上運(yùn)行的 ROS 節(jié)點(diǎn),ROSActivity.java:

package?com.example.hyperbot;import?android.os.Bundle;import?android.os.Handler;import?androidx.appcompat.app.AppCompatActivity;import?org.ros2.rcljava.RCLJava;import?org.ros2.rcljava.executors.Executor;import?org.ros2.rcljava.executors.SingleThreadedExecutor;import?java.util.Timer;import?java.util.TimerTask;public?class?ROSActivity?extends?AppCompatActivity?{
????private?Executor?rosExecutor;??//?ROS2?執(zhí)行器對(duì)象,用于處理節(jié)點(diǎn)的消息
????private?Timer?timer;??//?定時(shí)器對(duì)象,定時(shí)執(zhí)行節(jié)點(diǎn)的?spinSome()?方法
????private?Handler?handler;??//?Android?UI?線程的?Handler?對(duì)象,用于在?UI?線程上執(zhí)行節(jié)點(diǎn)的?spinSome()?方法

????private?static?String?logtag?=?ROSActivity.class.getName();

????private?static?long?SPINNER_DELAY?=?0;??//?定時(shí)器的啟動(dòng)延遲時(shí)間(單位:毫秒)
????private?static?long?SPINNER_PERIOD_MS?=?200;??//?定時(shí)器的周期時(shí)間(單位:毫秒)

????//?生命周期方法,當(dāng)活動(dòng)第一次創(chuàng)建時(shí)調(diào)用
????@Override
????public?void?onCreate(final?Bundle?savedInstanceState)?{
????????super.onCreate(savedInstanceState);
????????this.handler?=?new?Handler(getMainLooper());??//?創(chuàng)建?Android?UI?線程的?Handler?對(duì)象
????????RCLJava.rclJavaInit();??//?初始化?RCLJava?庫
????????this.rosExecutor?=?this.createExecutor();??//?創(chuàng)建?ROS2?執(zhí)行器對(duì)象
????}

????//?生命周期方法,當(dāng)活動(dòng)從暫停狀態(tài)恢復(fù)時(shí)調(diào)用
????@Override
????protected?void?onResume()?{
????????super.onResume();
????????timer?=?new?Timer();??//?創(chuàng)建定時(shí)器對(duì)象
????????timer.scheduleAtFixedRate(new?TimerTask()?{
????????????public?void?run()?{
????????????????Runnable?runnable?=?new?Runnable()?{
????????????????????public?void?run()?{
????????????????????????rosExecutor.spinSome();??//?在?UI?線程上執(zhí)行節(jié)點(diǎn)的?spinSome()?方法
????????????????????}
????????????????};
????????????????handler.post(runnable);??//?將?Runnable?對(duì)象提交到?UI?線程的消息隊(duì)列中,?避免在子線程中更新UI
????????????}
????????},?this.getDelay(),?this.getPeriod());??//?啟動(dòng)定時(shí)器,定時(shí)執(zhí)行節(jié)點(diǎn)的?spinSome()?方法
????}

????//?生命周期方法,當(dāng)活動(dòng)暫停時(shí)調(diào)用
????@Override
????protected?void?onPause()?{
????????super.onPause();
????????if?(timer?!=?null)?{
????????????timer.cancel();??//?取消定時(shí)器的執(zhí)行
????????}
????}

????public?void?run()?{
????????rosExecutor.spinSome();??//?執(zhí)行節(jié)點(diǎn)的?spinSome()?方法
????}

????public?Executor?getExecutor()?{
????????return?this.rosExecutor;??//?獲取?ROS2?執(zhí)行器對(duì)象
????}

????protected?Executor?createExecutor()?{
????????return?new?SingleThreadedExecutor();??//?創(chuàng)建單線程的?ROS2?執(zhí)行器對(duì)象
????}

????protected?long?getDelay()?{
????????return?SPINNER_DELAY;??//?獲取定時(shí)器的啟動(dòng)延遲時(shí)間
????}

????protected?long?getPeriod()?{
????????return?SPINNER_PERIOD_MS;??//?獲取定時(shí)器的周期時(shí)間
????}}

最后在MainActivity中創(chuàng)建新的發(fā)布和訂閱者節(jié)點(diǎn)對(duì)象,并將其添加到執(zhí)行器:

listenerNode?=?new?ListenerNode("ros2_humble_node_listener",?"/chatter",?listenerView);talkerNode?=?new?TalkerNode("ros2_humble_node_talker",?"/chatter");getExecutor().addNode(listenerNode);getExecutor().addNode(TalkerNode);

最后的運(yùn)行效果如下:

將手機(jī)和電腦連接到同一網(wǎng)絡(luò)下,可以在終端中看到手機(jī)端發(fā)布的話題及消息,如下圖:

至此,關(guān)于ros2 humble的Android版本的編譯以及測(cè)試到此結(jié)束;下一篇來聊聊如何開發(fā)APP來控制OriginBot的運(yùn)動(dòng),敬請(qǐng)期待。




在Ubuntu22.04下編譯ros2 humble的Android版本的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國家法律
安庆市| 麦盖提县| 奉新县| 德兴市| 阿尔山市| 奉新县| 略阳县| 芒康县| 沙湾县| 湖北省| 左权县| 庄浪县| 嘉善县| 阳山县| 齐河县| 镇巴县| 达拉特旗| 承德市| 平利县| 江口县| 衡阳市| 山阳县| 卢氏县| 全南县| 友谊县| 丽江市| 乐都县| 时尚| 南溪县| 定边县| 佛坪县| 中卫市| 于都县| 三门县| 白城市| 嘉鱼县| 乌审旗| 桐柏县| 元氏县| 临江市| 岳普湖县|