Gazebo环境下基于ROS和OpenCV的阿克曼小车综合实验

本文涉及的产品
资源编排,不限时长
简介: 单车综合

Gazebo环境下基于ROS和OpenCV的阿克曼小车综合实验

1. 创建资源

开始实验之前,您需要先创建实验相关资源。

  1. 在实验室页面,单击创建资源
  2. (可选)在实验室页面左侧导航栏中,单击云产品资源列表,可查看本次实验资源相关信息(例如IP地址、子用户信息等)。

说明:资源创建过程需要3~5分钟视资源不同开通时间有所差异,ACK等资源开通时间较长。完成实验资源的创建后,您可以在云产品资源列表查看已创建的资源信息,例如:子用户名称、子用户密码、AK ID、AK Secret、资源中的项目名称等。

实验环境一旦开始创建则进入计时阶段,建议学员先基本了解实验具体的步骤、目的,真正开始做实验时再进行创建。

资源创建成功,可在左侧的资源卡片中查看相关资源信息以及RAM子账号信息


2. 进入Docker镜像

打开火狐浏览器。

通过RAM用户名密码登录页面,输入子用户密码登录账号。

登陆后选择左侧产品与服务中的云服务器ECS。

在所有云服务器中通过左下角地域找到正确的服务器并点击远程连接。

选择通过VNC远程连接。

选择重置VNC密码并设置新的密码后登录。

设置好进入各选项后进入Ubuntu桌面。

右键打开终端,输入sudo docker run -it --rm -p 6080:80 aliyun_demo命令运行镜像aliyun_demo。

运行成功后打开浏览器,在地址一栏输入127.0.0.1:6080进入镜像系统。


3. 代码部分

单车综合程序使用的是raceworld1场景,由raceworld1.launch文件启动。找到/root/aliyun_demo/src/raceworld-master/launch文件夹中的raceworld1.launch文件并打开。

在launch文件内加载了多个控制器,其中left_rear_wheel_velocity_controller, right_rear_wheel_velocity_controller,left_front_wheel_velocity_controller,right_front_wheel_velocity_controller,left_steering_hinge_position_controller和right_steering_hinge_position_controller接收ROS消息,分别控制了左右后轮速度,左右前轮速度和转向角度。

<node name="controller_manager" pkg="controller_manager" type="spawner" 
respawn="false" output="screen"  
args="left_rear_wheel_velocity_controller       right_rear_wheel_velocity_controller
left_front_wheel_velocity_controller      right_front_wheel_velocity_controller
left_steering_hinge_position_controller   right_steering_hinge_position_controller
joint_state_controller"/>

在加载控制器代码的下面,还加载了一个名为servo_comannds1.py的代码文件。

<nodepkg="raceworld" type="servo_commands.py" name="servo_commands" output="screen" >

打开/root/aliyun_demo/src/raceworld/scripts文件夹中的servo_comannds1.py文件我们可以看到,代码中新建了一个名为servo_comannds的节点,他订阅并接收/deepracer1/ackermann_cmd_mux/output消息。

def servo_commands():
    rospy.init_node('servo_commands', anonymous=True)
    robot_name = rospy.get_param('~robot_name')
    rospy.Subscriber("ackermann_cmd_mux/output", AckermannDriveStamped, set_throttle_steer)
    # spin() simply keeps python from exiting until this node is stopped
    rospy.spin()
if __name__ == '__main__':
    try:
        servo_commands()
    except rospy.ROSInterruptException:
        pass

在接收到消息后,经过适当的转换,将速度和转向信息发布给之前所述六个控制器。

def set_throttle_steer(data):
    global flag_move
    pub_vel_left_rear_wheel = rospy.Publisher("left_rear_wheel_velocity_controller/command", Float64, queue_size=1)
    pub_vel_right_rear_wheel = rospy.Publisher("right_rear_wheel_velocity_controller/command", Float64, queue_size=1)
    pub_vel_left_front_wheel = rospy.Publisher("left_front_wheel_velocity_controller/command", Float64, queue_size=1)
    pub_vel_right_front_wheel = rospy.Publisher("right_front_wheel_velocity_controller/command", Float64, queue_size=1)
    pub_pos_left_steering_hinge = rospy.Publisher("left_steering_hinge_position_controller/command", Float64, queue_size=1)
    pub_pos_right_steering_hinge = rospy.Publisher("right_steering_hinge_position_controller/command", Float64, queue_size=1)
    throttle = data.drive.speed*28
    steer = data.drive.steering_angle
    pub_vel_left_rear_wheel.publish(throttle)
    pub_vel_right_rear_wheel.publish(throttle)
    pub_vel_left_front_wheel.publish(throttle)
    pub_vel_right_front_wheel.publish(throttle)
    pub_pos_left_steering_hinge.publish(steer)
    pub_pos_right_steering_hinge.publish(steer)

在launch文件中,还加载了一个名为control_servo.py的文件。

<nodepkg="raceworld" type="control_servo.py" name="control_servo" output="screen"/>

打开该文件,可以看到新建了一个名为control_servo的节点,并订阅了Twist类型的cmd_akm1消息。

def cmd_to_akm():
    rospy.init_node('control_servo', anonymous=True)
    rospy.Subscriber("cmd_akm1", Twist, callback1)
    rospy.spin()
    pass
if __name__ =='__main__':
    cmd_to_akm()
    pass

当接收到订阅的消息后,新建/deepracer1/ackermann_cmd_mux/output主题的发布者,并将AckermannDriveStamped类型的消息数据设置为合适的值后发布出去。

def callback1(msg):
    pub = rospy.Publisher("/deepracer1/ackermann_cmd_mux/output", AckermannDriveStamped, queue_size=10)
    akm = AckermannDriveStamped()
    if msg.linear.x != 0:
        akm.drive.speed = msg.linear.x*1.80
        akm.drive.steering_angle = atan((msg.angular.z/msg.linear.x)*0.133)
    else:
        akm.drive.speed = 0
        akm.drive.steering_angle = 0
    print("Speed:",akm.drive.speed)
    print("Steering_Angle:",akm.drive.steering_angle)
    print("Message From control_servo.py Published\n")
    pub.publish(akm)
    pass

巡线程序需要运行/root/aliyun_demo/src/raceworld/scripts/0final/singleCar_changePath文件夹中的running_car2.py文件。在代码中首先新建了get_camera节点,并且订阅了名为/deepracer1/camera/zed_left/image_rect_color_left的消息。该消息通过Gazebo中的deepracer小车上的左摄像头获取。在获取到摄像头图像信息后,把它转换为cv2图像数据。

def image_callback(msg):
    bridge = cv_bridge.CvBridge()
    frame = bridge.imgmsg_to_cv2(msg, 'bgr8')
    follow_line(frame, "yellow")
    pass
if __name__ == '__main__':
    rospy.init_node("get_camera")
    rospy.Subscriber("/deepracer1/camera/zed_left/image_rect_color_left", Image, image_callback)
    rospy.spin()
    pass

在follow_line_out和follow_line_in函数中,先对图像进行HSV转换和二值图转换操作。

cmd_vel_pub = rospy.Publisher("cmd_akm1", Twist, queue_size=10)
kernel = np.ones((7,7), np.uint8)
#转换为HSV图像
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
lower_yellow = np.array([26, 43, 46])
lower_yellow = np.array([26, 43, 0])
upper_yellow = np.array([34, 255, 255])
#对车道线图像二值化
mask1 = cv2.inRange(hsv, lower_yellow, upper_yellow)
h, w = mask1.shape

HSV图如下所示。

二值图如下所示。

接着进行膨胀操作,并分别设置车道线和车道边界的感兴趣区域(ROI)。

#膨胀操作
mask1 = cv2.dilate(mask1, kernel, 2)
#设置感兴趣区域ROI
mask1 = set_roi_forward1(h, w, mask1)
lower_gray = np.array([0, 0, 120])
upper_gray = np.array([0, 0, 200])
#对车道边界二值化
mask2 = cv2.inRange(hsv, lower_gray, upper_gray)
#膨胀操作
mask2 = cv2.dilate(mask2, kernel, 2)
#设置感兴趣区域ROI
mask2 = set_roi_forward2(h, w, mask2)
#cv2.namedWindow("mask1",0)
#cv2.imshow("mask1", mask1)
#cv2.namedWindow("mask2",0)
#cv2.imshow("mask2", mask2)

ROI图像如下所示。可以看到感兴趣区域只显示小车镜头前的一部份区域。

接着调用cv2.moments函数获得图像的矩并计算质心,根据质心与图像中线的偏移设置转角,并设置速度。

#获得图像矩
M1 = cv2.moments(mask1)
M2 = cv2.moments(mask2)
if M1['m00'] == 0:
    cx1 = 630
    if M2['m00'] > 0:
        cx2 = int(M2['m10'] / M2['m00'])
    elif M2['m00'] == 0:
        cx2 = 0
    cy = 0
    pass
elif M1['m00'] > 0:
    cx1 = int(M1['m10'] / M1['m00'])
    if M2['m00'] > 0:
        cx2 = int(M2['m10'] / M2['m00'])
    elif M2['m00'] == 0:
        cx2 = 0
    cy = int(M1['m01'] / M1['m00'])
cx = int((cx1-cx2)*50/64+cx2)
#在质心画圆
cv2.circle(image, (cx, cy), 20, (0, 0, 255), -1)
err = cx - w / 2 - 60
twist = Twist()
twist.linear.x = 0.05
#根据质心与图像中线的偏移设置转角
twist.angular.z = -float(err / 5.0) / 50
cmd_vel_pub.publish(twist)
cv2.namedWindow("Camera",0)
cv2.imshow("Camera", image)

同时以质心为中心点在原始图像上绘制红色的圆形图案作为参考,如图所示。

在行驶过程中,对摄像头获取到的图像信息与本地路牌图片进行模板匹配并获得其中最大值。

def do_match(frame):
    max = 0.0
    #读取本地图片
    template = cv2.imread(r"/home/chouu/aliyun_demo/src/raceworld/scripts/01.png")
    #摄像头图像与本地图片进行模板匹配,返回一个矩阵,每个像素值代表匹配程度
    res = cv2.matchTemplate(frame, template, cv2.TM_CCOEFF_NORMED)
    #获得矩阵中最大值及位置和最小值及位置
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
    if max_val>max:
        max = max_val
    return max
    pass

当最大值达到一定程度后表示识别到停车指示路牌,调用stop函数实现停车。

match = do_match(img)
#print(match)
#由于行驶在内道,距离路牌较远,所以匹配值要求降低
if match > 0.57:
    stop(1)
    print("stop now!")
    time.sleep(3)
    rospy.signal_shutdown("~~~")
#停止行驶
def stop(id):
    index = id
    cmd_vel_pub = rospy.Publisher("cmd_akm"+str(index), Twist, queue_size=10)
    twist = Twist()
    twist.linear.x = 0.0
    twist.angular.z = 0.0
    for i in range(20):
        cmd_vel_pub.publish(twist)
    time.sleep(2)

同时调用opencvdetect函数识别地面上的二维码,当获取到的二维码ID与当前ID不同时,小车开始变道行驶。

if get_tag(img):
    delay=0
    if id != pre_id:
        pre_id = id
        change_path()
    else:
        pass
#换道
def change_path():
    global path
    if path == 0:
        path = 1
    else:
        path = 0
#读取二维码
def get_tag(frame):
    global id
    global tag_flag 
    global delay
    detector = apriltag.Detector()
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    tags = detector.detect(gray)
    if tags == []:
        tag_flag=0
        return False
    else:
        delay = 0
        tag_flag = 1
        for tag in tags:
            print("Tag detected with id: ", tag.tag_id)
            id = tag.tag_id
        return True
    pass


4. 实验操作

打开终端输入如图命令,启动raceworld1场景。

cd aliyun_demo
source devel/setup.bash
roslaunch raceworld_master raceworld1.launch

打开新的终端输入如图所示命令,开启巡线程序。

cd aliyun_demo
source devel/setup.bash
rosrun raceworld_master running_car2.py

此时可以观察到小车的运行状况。当识别到地面二维码时,将当前行驶车道输出于终端。

当识别到停车路牌时,小车自动停止,并中断程序运行。

实验链接:https://developer.aliyun.com/adc/scenario/788e5f50f03049069ca60dc24ec381c2

相关实践学习
使用ROS创建VPC和VSwitch
本场景主要介绍如何利用阿里云资源编排服务,定义资源编排模板,实现自动化创建阿里云专有网络和交换机。
阿里云资源编排ROS使用教程
资源编排(Resource Orchestration)是一种简单易用的云计算资源管理和自动化运维服务。用户通过模板描述多个云计算资源的依赖关系、配置等,并自动完成所有资源的创建和配置,以达到自动化部署、运维等目的。编排模板同时也是一种标准化的资源和应用交付方式,并且可以随时编辑修改,使基础设施即代码(Infrastructure as Code)成为可能。 产品详情:https://www.aliyun.com/product/ros/
相关文章
|
1月前
|
Ubuntu Linux 编译器
Linux/Ubuntu下使用VS Code配置C/C++项目环境调用OpenCV
通过以上步骤,您已经成功在Ubuntu系统下的VS Code中配置了C/C++项目环境,并能够调用OpenCV库进行开发。请确保每一步都按照您的系统实际情况进行适当调整。
274 3
|
1月前
|
弹性计算 JSON 关系型数据库
使用ROS模板基于ECS和RDS创建WordPress环境
使用ROS模板基于ECS和RDS创建WordPress环境
|
3月前
|
Ubuntu Shell C++
在Ubuntu18.04上安装ros2的环境,ros2的常用命令:播放包、录制包等
在Ubuntu18.04上安装ros2的环境,ros2的常用命令:播放包、录制包等
176 1
|
4月前
|
Ubuntu 机器人 Shell
ubuntu20.04创建ros环境、创建rospackage
至此,我们已经详细讲解了在Ubuntu 20.04上创建ROS环境及ROS包的步骤。这为进一步的机器人软件开发奠定了坚实的基础。
187 1
|
4月前
|
Ubuntu 机器人 Shell
ubuntu20.04创建ros环境、创建rospackage
至此,我们已经详细讲解了在Ubuntu 20.04上创建ROS环境及ROS包的步骤。这为进一步的机器人软件开发奠定了坚实的基础。
62 1
|
4月前
|
机器人 Shell 开发者
`roslibpy`是一个Python库,它允许非ROS(Robot Operating System)环境(如Web浏览器、移动应用等)与ROS环境进行交互。通过使用`roslibpy`,开发者可以编写Python代码来远程控制ROS节点,发布和订阅话题,以及调用服务。
`roslibpy`是一个Python库,它允许非ROS(Robot Operating System)环境(如Web浏览器、移动应用等)与ROS环境进行交互。通过使用`roslibpy`,开发者可以编写Python代码来远程控制ROS节点,发布和订阅话题,以及调用服务。
|
1月前
|
计算机视觉
Opencv学习笔记(三):图像二值化函数cv2.threshold函数详解
这篇文章详细介绍了OpenCV库中的图像二值化函数`cv2.threshold`,包括二值化的概念、常见的阈值类型、函数的参数说明以及通过代码实例展示了如何应用该函数进行图像二值化处理,并展示了运行结果。
332 0
Opencv学习笔记(三):图像二值化函数cv2.threshold函数详解
|
2月前
|
算法 计算机视觉
opencv图像形态学
图像形态学是一种基于数学形态学的图像处理技术,它主要用于分析和修改图像的形状和结构。
49 4
|
2月前
|
存储 计算机视觉
Opencv的基本操作(一)图像的读取显示存储及几何图形的绘制
本文介绍了使用OpenCV进行图像读取、显示和存储的基本操作,以及如何绘制直线、圆形、矩形和文本等几何图形的方法。
Opencv的基本操作(一)图像的读取显示存储及几何图形的绘制
|
3月前
|
算法 计算机视觉 Python
python利用opencv进行相机标定获取参数,并根据畸变参数修正图像附有全部代码(流畅无痛版)
该文章详细介绍了使用Python和OpenCV进行相机标定以获取畸变参数,并提供了修正图像畸变的全部代码,包括生成棋盘图、拍摄标定图像、标定过程和畸变矫正等步骤。
python利用opencv进行相机标定获取参数,并根据畸变参数修正图像附有全部代码(流畅无痛版)

推荐镜像

更多