0
点赞
收藏
分享

微信扫一扫

ROS与Python3


一、Anaconda与Virtualenv

虚拟环境Anaconda与Virtualenv可以二选一

1.1 ​​Anaconda​​

Tip:ros 和Anaconda 一起使用的时候,如果先安装了Anaconda,再安装ros,会报错,因此正确的联合使用方式为:先安装ros相关,后安装anaconda即可。
Anaconda安装完成后会在~/.bashrc中写入如下命令:

# >>> conda initialize >>>
# !! Contents within this block are managed by ‘conda init‘ !!
__conda_setup="$(‘/home/“user”/anaconda3/bin/conda‘ ‘shell.bash‘ ‘hook‘ 2> /dev/null)"
if [ $? -eq 0 ]; then
eval "$__conda_setup"
else
if [ -f "/home/"user"/anaconda3/etc/profile.d/conda.sh" ]; then
. "/home/"user"/anaconda3/etc/profile.d/conda.sh"
else
export PATH="/home/"user"/anaconda3/bin:$PATH"
fi
fi
unset __conda_setup
# <<< conda initialize <<<

ROS与Python3_虚拟环境

脚本命令解析:

$ if [ $? -eq 0 ]; then echo "true"; else echo "false"; fi;
#这条命令的意思是:如果上一条命令执行成功则打印true,否则打印false
#对于上述~/.bashrc中的命令,通常的结果是执行eval "$__conda_setup"
#除非__conda_setup="$('/home/jay/anaconda3/bin/conda' 'shell.bash' 'hook' 2> /dev/null)"这一句出现了错误

可以通过在终端运行​​$ echo "$('/home/jay/anaconda3/bin/conda' 'shell.bash' 'hook' 2> /dev/null)"​​​命令来查看​​eval "$__conda_setup"​​​都干了哪些事。你也可以运行​​$ cat /home/jay/anaconda3/etc/profile.d/conda.sh​​​看一下这个脚本里的内容,没错,二者做了相同的工作,只不过前者比后者多执行了一步​​conda activate base​​​。​​if [ -f "/home/jay/anaconda3/etc/profile.d/conda.sh" ]​​;的意思是判断这个脚本文件是否存在且没有被损坏,通常这个条件也是满足的。

博主想要表达的意思是,只有当前两个条件都不满足时才会执行​​export PATH="/home/jay/anaconda3/bin:$PATH"​​,可见这个操作并不是上上之选。然而很多人在装完Anaconda后会直接将anaconda的bin目录加入环境变量,鄙人认为这是不科学的,且事实证明这样做会经常带来一些问题,因为它干扰了系统原生的环境。

按照Anaconda默认的配置,打开一个新的终端会自动进入Anaconda的环境:

ROS与Python3_anaconda_02


如果不想直接进入Anaconda的环境,博主的做法是在~/.bashrc中添加一句​​conda deactivate​​,这样打开一个新的终端就是系统原始的Python环境:

ROS与Python3_python_03


要再进入Anaconda环境就运行​​$ conda activate​​。

ROS与Python3_未定义_04


上图对比了进入anaconda环境与退出anaconda环境后,系统环境变量发生的变化。显然,在进入anaconda环境后,PATH中多了/home/jay/anaconda3/bin,而退出anaconda环境后/home/jay/anaconda3/bin又消失了,博主认为这才是使用Anaconda的正确姿势。

利用Anaconda创建自己的虚拟环境:

$ conda create -n py3.7 python=3.7
$ conda activate py3.7

ROS与Python3_linux_05

1.2 Virtualenv

安装Virtualenv:

$ sudo pip install virtualenv

创建虚拟Python环境:

$ whereis python2
$ whereis python3
$ mkdir ~/virtualenv
$ virtualenv -p /usr/bin/python3.5 ~/virtualenv/py3.5
$ source ~/virtualenv/py3.5/bin/activate

ROS与Python3_未定义_06


可以在虚拟环境中装个TensorFlow玩一下:

$ pip install tensorflow -i https://pypi.tuna.tsinghua.edu.cn/simple

二、在Python3环境下编译ROS包

为了说明问题,我们使用OpenCV在两个虚拟环境中折腾一下:用Anaconda创建的py3.7编写一个OpenCV的Python节点来发布图像话题,然后编写一个C++节点用来订阅图像话题并显示,在Virtualenv创建的py3.5环境下进行编译。具体步骤如下:

首先创建一个ROS工作空间并新建一个ROS包:

$ mkdir -p ~/ros_ws/src
$ cd ~/ros_ws/src
$ catkin_init_workspace
$ catkin_create_pkg cv_package roscpp cv_bridge OpenCV
$ cd cv_package/src
$ touch pub_image.py sub_image.cpp

然后在pub_image.py中粘贴以下内容:

#!/home/jay/anaconda3/envs/py3.7/bin/python
#-*-coding:utf-8-*-
#注意这里import的顺序
import numpy
import rospy
from sensor_msgs.msg import Image
import sys
sys.path.remove('/opt/ros/kinetic/lib/python2.7/dist-packages')
import cv2


def shutdown():
print("shut down!")


def publisher():
rospy.init_node("image_publisher", anonymous=True)
if len(sys.argv)<2:
rospy.loginfo("There should be a parameter follow the %s, such as 0 or 1.", sys.argv[0])
rospy.on_shutdown(shutdown)
return
capture=cv2.VideoCapture(int(sys.argv[1]))
imgPub=rospy.Publisher('/camera/image_raw',Image,queue_size=1)
rospy.loginfo("I am publishing an image...")
rate = rospy.Rate(30)

while not rospy.is_shutdown():
ref,frame=capture.read()
image=Image()
image.encoding='bgr8'
image.height=frame.shape[0]
image.width=frame.shape[1]
image.step=frame.shape[1]*frame.shape[2]
image.data=numpy.array(frame).tostring()
image.header.stamp=rospy.Time.now()
imgPub.publish(image)
rate.sleep()


if __name__ == '__main__':
try:
publisher()
except rospy.ROSInterruptException:
pass

在sub_image.cpp中粘贴以下内容:

#include <ros/ros.h>
#include <cv_bridge/cv_bridge.h>
#include <opencv2/highgui/highgui.hpp>

void subscriber(const sensor_msgs::ImageConstPtr& msg)
{
cv::Mat img;
cv_bridge::CvImageConstPtr cv_ptr;
try
{
cv_ptr = cv_bridge::toCvShare(msg);
}
catch (cv_bridge::Exception& e)
{
ROS_ERROR("cv_bridge exception: %s", e.what());
return;
}
cv_ptr->image.copyTo(img);
cv::imshow("image",img);
if(27==cv::waitKey(10))ros::shutdown();
}

int main(int argc, char** argv)
{
ros::init(argc, argv, "image_subscriber");
ros::NodeHandle nh;
if(!nh.ok())return 0;
ros::Subscriber imgSub = nh.subscribe("camera/image_raw", 10, subscriber);
ROS_INFO("I am subscribing an image...");
while (ros::ok())ros::spin();
cv::destroyAllWindows();
return 0;
}

再修改一下CMakeLists.txt:

cmake_minimum_required(VERSION 2.8.3)
project(cv_package)

## Compile as C++11, supported in ROS Kinetic and newer
add_compile_options(-std=c++11)

set(OpenCV_DIR /opt/ros/kinetic/share/OpenCV-3.3.1-dev/)
find_package(catkin REQUIRED COMPONENTS
OpenCV
cv_bridge
roscpp
)

catkin_package(
)

include_directories(
${catkin_INCLUDE_DIRS}
)

add_executable(sub_image src/sub_image.cpp)
target_link_libraries(sub_image
${catkin_LIBRARIES}
)

最后在py3.5环境中进行编译(编译本身和Python3.5并没有关系,这里只是用到了它的环境)

$ source ~/virtualenv/py3.5/bin/activate
$ cd ~/ros_ws
$ catkin_make #不出意外的话这一步会报错,然后按照以下步骤进行编译
$ pip install catkin_pkg pyyaml empy rospkg numpy
$ rm -rf build
$ catkin_make
$ chmod +x src/cv_package/src/pub_image.py
$ echo "source ~/ros_ws/devel/setup.bash" >> ~/.bashrc

先运行$ roscore,再运行pub_image和sub_image这两个节点:

# 运行pub_image.py之前,要先在py3.7环境下安装一些依赖
$ conda activate py3.7
$ pip install numpy pyyaml rospkg opencv_python -i https://pypi.tuna.tsinghua.edu.cn/simple
$ conda deactivate #这一步也可以不执行
$ rosrun cv_package pub_image.py 0
# 打开一个新的终端
$ rosrun cv_package sub_image

三、解决相关的疑难杂症
opencv常见症状如下:

/opt/ros/kinetic/lib/x86_64-linux-gnu/libopencv_imgcodecs3.so.3.3.1:对‘TIFFReadRGBAStrip@LIBTIFF_4.0’未定义的引用
/opt/ros/kinetic/lib/x86_64-linux-gnu/libopencv_imgcodecs3.so.3.3.1:对‘TIFFReadDirectory@LIBTIFF_4.0’未定义的引用
/opt/ros/kinetic/lib/x86_64-linux-gnu/libopencv_imgcodecs3.so.3.3.1:对‘TIFFWriteEncodedStrip@LIBTIFF_4.0’未定义的引用
/opt/ros/kinetic/lib/x86_64-linux-gnu/libopencv_imgcodecs3.so.3.3.1:对‘TIFFIsTiled@LIBTIFF_4.0’未定义的引用
/opt/ros/kinetic/lib/x86_64-linux-gnu/libopencv_imgcodecs3.so.3.3.1:对‘TIFFWriteScanline@LIBTIFF_4.0’未定义的引用
/opt/ros/kinetic/lib/x86_64-linux-gnu/libopencv_imgcodecs3.so.3.3.1:对‘TIFFGetField@LIBTIFF_4.0’未定义的引用
/opt/ros/kinetic/lib/x86_64-linux-gnu/libopencv_imgcodecs3.so.3.3.1:对‘TIFFNumberOfStrips@LIBTIFF_4.0’未定义的引用
/opt/ros/kinetic/lib/x86_64-linux-gnu/libopencv_imgcodecs3.so.3.3.1:对‘TIFFScanlineSize@LIBTIFF_4.0’未定义的引用
/opt/ros/kinetic/lib/x86_64-linux-gnu/libopencv_imgcodecs3.so.3.3.1:对‘TIFFReadEncodedTile@LIBTIFF_4.0’未定义的引用
/opt/ros/kinetic/lib/x86_64-linux-gnu/libopencv_imgcodecs3.so.3.3.1:对‘TIFFReadRGBATile@LIBTIFF_4.0’未定义的引用
/opt/ros/kinetic/lib/x86_64-linux-gnu/libopencv_imgcodecs3.so.3.3.1:对‘TIFFClose@LIBTIFF_4.0’未定义的引用
/opt/ros/kinetic/lib/x86_64-linux-gnu/libopencv_imgcodecs3.so.3.3.1:对‘TIFFClientOpen@LIBTIFF_4.0’未定义的引用
/opt/ros/kinetic/lib/x86_64-linux-gnu/libopencv_imgcodecs3.so.3.3.1:对‘TIFFRGBAImageOK@LIBTIFF_4.0’未定义的引用
/opt/ros/kinetic/lib/x86_64-linux-gnu/libopencv_imgcodecs3.so.3.3.1:对‘TIFFOpen@LIBTIFF_4.0’未定义的引用
/opt/ros/kinetic/lib/x86_64-linux-gnu/libopencv_imgcodecs3.so.3.3.1:对‘TIFFReadEncodedStrip@LIBTIFF_4.0’未定义的引用
/opt/ros/kinetic/lib/x86_64-linux-gnu/libopencv_imgcodecs3.so.3.3.1:对‘TIFFSetField@LIBTIFF_4.0’未定义的引用
/opt/ros/kinetic/lib/x86_64-linux-gnu/libopencv_imgcodecs3.so.3.3.1:对‘TIFFSetWarningHandler@LIBTIFF_4.0’未定义的引用
/opt/ros/kinetic/lib/x86_64-linux-gnu/libopencv_imgcodecs3.so.3.3.1:对‘TIFFSetErrorHandler@LIBTIFF_4.0’未定义的引用

解决办法:修改CMakeLists.txt,在target_link_libraries中添加​​/usr/lib/x86_64-linux-gnu/libtiff.so​​,如下:

add_executable(${PROJECT_NAME}_node src/main.cpp)
target_link_libraries(${PROJECT_NAME}_node
${catkin_LIBRARIES}
/usr/lib/x86_64-linux-gnu/libtiff.so
)

安装python3环境中的问题参考链接

tip:如果要使用cv_bridge,则需要参考另一篇​​文章​​的配置


举报

相关推荐

0 条评论