9.自主驾驶

在接下来的环节中,我们要实现漫游者号的自动驾驶功能。

完成这个功能我们需要四个程序,第一个为感知程序,其对摄像头输入的图片进行变换处理和坐标变换使用。第二个程序为决策程序,功能是帮助漫游者号根据当前条件和状态进行相应的决策,以实现漫游者号前进,后退,转向等功能。第三个是支持程序,来定义一些关于漫游者号状态的类等。最后为主程序,来调用三个函数对漫游者号进行控制的。

Udacity提供的初始程序在Udacity机器人软件工程师课程笔记(三)中有所提供。

经过前面的学习,我们已经清楚了感知程序和决策程序,因为之前我都把它写成了一个程序,这样导致程序显得非常复杂,可读性不是太高。现在针对各个功能程序进行程序的划分,来方便最后的主程序的调用。以下的初始程序都放在RoboND-Rover-Project文件夹下的code文件夹中。

(1) 感知部分perception.py

首先是感知部分。感知部分perception.py应当具有三个功能,

  1. 图像阈值处理
  2. 透视变换
  3. 坐标变换
    这些功能在之前的程序中已经多次使用了,但是我们需要调用这些功能去编写一个综合感知函数 perception_step() 来对图像进行处理,它需要完成以下功能:
    1)定义透视变换的源点和目标点
    2)应用透视变换
    3)应用颜色阈值来识别可导航的地形/障碍物/岩石样本
    4)更新Rover.vision_image(这个图像显示在屏幕的左侧)
    5)将地图图像像素值转换为以漫游者号为中心的坐标
    6)将以漫游者号为中心的像素值坐标转换为世界坐标
    7)更新漫游者号的世界地图(这个图像显示在屏幕右侧)
    8)将以漫游者号为中心的像素位置转换为极坐标

    感知部分的程序如下
import numpy as np
import cv2
import matplotlib.pyplot as plt##图像处理
# 定义二值化图像函数
def color_thresh(img, rgb_thresh=(160, 160, 160)):img_thresh = np.zeros_like(img[:, :, 0])above_thresh = (img[:, :, 0] > rgb_thresh[0]) \& (img[:, :, 1] > rgb_thresh[1]) \& (img[:, :, 2] > rgb_thresh[2])img_thresh[above_thresh] = 255return img_thresh##透视变换
# 定义图像映射函数,将摄像头的图像映射到平面坐标中去
def perspect_transform(img, src, dst):M = cv2.getPerspectiveTransform(src, dst)  # 定义变换矩阵img_perspect = cv2.warpPerspective(img, M, (img.shape[1], img.shape[0]))return img_perspect##坐标变换
# 定义从图像坐标转换成漫游者号坐标函数
def rover_coords(binary_img):ypos, xpos = binary_img.nonzero()x_pixel = -(ypos - binary_img.shape[0]).astype(np.float)y_pixel = -(xpos - binary_img.shape[1]/2 ).astype(np.float)return x_pixel, y_pixel# 定义旋转操作函数
def rotate_pix(xpix, ypix, yaw):yaw_rad = yaw * np.pi / 180xpix_rotated = (xpix * np.cos(yaw_rad)) - (ypix * np.sin(yaw_rad))ypix_rotated = (xpix * np.sin(yaw_rad)) + (ypix * np.cos(yaw_rad))return xpix_rotated, ypix_rotated# 定义平移操作函数
def translate_pix(xpix_rot, ypix_rot, xpos, ypos, scale):xpix_translated = (xpix_rot / scale) + xposypix_translated = (ypix_rot / scale) + yposreturn xpix_translated, ypix_translated# 定义综合函数,将旋转和平移函数进行结合,并限制了图像范围
def pix_to_world(xpix, ypix, xpos, ypos, yaw, world_size, scale):xpix_rot, ypix_rot = rotate_pix(xpix, ypix, yaw)xpix_tran, ypix_tran = translate_pix(xpix_rot, ypix_rot, xpos, ypos, scale)x_pix_world = np.clip(np.int_(xpix_tran), 0, world_size - 1)y_pix_world = np.clip(np.int_(ypix_tran), 0, world_size - 1)return x_pix_world, y_pix_world# 定义转换为极坐标函数
def to_polar_coords(xpix, ypix):dist = np.sqrt(xpix**2 + ypix ** 2)angles = np.arctan2(ypix, xpix)return dist, anglesdef perception_step(Rover):# (1)定义透视变换的原点和目标点# 参考图像filename = 'E:/2019/RoboND-Rover-Project-master/calibration_images/example_grid1.jpg'image = cv2.imread(filename)dst_size = 5bottom_offset = 0src = np.float32([[14, 140], [301, 140], [200, 96], [118, 96]])dst = np.float32([[image.shape[1] / 2 - dst_size, image.shape[0] - bottom_offset],[image.shape[1] / 2 + dst_size, image.shape[0] - bottom_offset],[image.shape[1] / 2 + dst_size, image.shape[0] - 2 * dst_size - bottom_offset],[image.shape[1] / 2 - dst_size, image.shape[0] - 2 * dst_size - bottom_offset],])# (2)应用透视变换img_perspect = perspect_transform(Rover.img, src, dst)# (3)应用颜色阈值来识别可导航的地形/障碍物/岩石样本img_thresh_ter = color_thresh(img_perspect, rgb_thresh=(160, 160, 160))img_thresh_obs = color_thresh(img_perspect, rgb_thresh=(160, 160, 160))img_thresh_roc = color_thresh(img_perspect, rgb_thresh=(160, 130, 0))# (4)更新ROver.vision_imageRover.vision_image[:, :, 0] = img_thresh_rocRover.vision_image[:, :, 1] = img_thresh_obsRover.vision_image[:, :, 2] = img_thresh_ter# (5)将地图像素值转换为以漫游者号为中心的坐标xpix, ypix = rover_coords(img_thresh_ter)# (6)将以漫游者号为中心的像素值坐标转化为世界地图坐标x_pix_world, y_pix_world = pix_to_world(xpix, ypix, xpos=Rover.pos[0], ypos=Rover.pos[1], yaw=Rover.yaw, world_size=200, scale=10)# (7)更新漫游者号的世界地图Rover.worldmap[x_pix_world, y_pix_world] += 1# (8)将以漫游者号为中心的像素位置转换为极坐标rover_distances, rover_angles = to_polar_coords(xpix, ypix)# 可导航地形像素的角度的数组Rover.nav_angles = rover_angles# 获取转向角,使用极坐标系角度的平均值,这样可以运行,但是会有意外情况avg_angle_degrees = np.mean((rover_angles ) * 180 / np.pi)Rover.steer= np.clip(avg_angle_degrees, -15, 15)# 获取油门速度,使用平均极坐标系的平均值Rover.throttle = np.mean(rover_distances)print('steer:', Rover.steer, 'Rover.throttle:', Rover.throttle)return Rover

(2) 决策部分decision.py

使用决策树逻辑编写的简单判别系统,控制漫游者号对目前的状态和环境做出相应的判断,并执行相应的加速,转向,停止等操作。

import numpy as np# 命令基于perception_step()函数的输出
def decision_step(Rover):if Rover.nav_angles is not None:# 检查 Rover.mode 状态if Rover.mode == 'forward':# 检查可导航地形的范围if len(Rover.nav_angles) >= Rover.stop_forward:  # 如果为forward模式,可导航地图是好的,速度如果低于最大值,则加速if Rover.vel < Rover.max_vel:# 设置油门值Rover.throttle = Rover.throttle_setelse:Rover.throttle = 0Rover.brake = 0# 将转向设置为平均夹角,范围为 +/- 15Rover.steer = np.clip(np.mean(Rover.nav_angles * 180/np.pi), -15, 15)# 如果缺少地形图,则设置为停止模式elif len(Rover.nav_angles) < Rover.stop_forward:# 设置到停止模式并刹车Rover.throttle = 0# 设置制动值Rover.brake = Rover.brake_setRover.steer = 0Rover.mode = 'stop'# 如果我们已经处于“停止”模式,那么做出不同的决定elif Rover.mode == 'stop':# 如果我们处于停止模式但仍然继续制动if Rover.vel > 0.2:Rover.throttle = 0Rover.brake = Rover.brake_setRover.steer = 0# 如果我们没有移动(vel <0.2),那就做点别的elif Rover.vel <= 0.2:# 现在为停止状态,依靠视觉数据,判断是否有前进的道路if len(Rover.nav_angles) < Rover.go_forward:Rover.throttle = 0# 松开制动器以便转动Rover.brake = 0# 转弯范围为+/- 15度,当停止时,下一条线将引起4轮转向Rover.steer = -15#如果停下来但看到前面有足够的可导航地形,那就启动if len(Rover.nav_angles) >= Rover.go_forward:# 将油门设置回存储值Rover.throttle = Rover.throttle_setRover.brake = 0# 将转向设置为平均角度Rover.steer = np.clip(np.mean(Rover.nav_angles * 180/np.pi), -15, 15)Rover.mode = 'forward'else:Rover.throttle = Rover.throttle_setRover.steer = 0Rover.brake = 0# 在可拾取岩石的状态下发送拾取命if Rover.near_sample and Rover.vel == 0 and not Rover.picking_up:Rover.send_pickup = Truereturn Rover

(3)项目支持函数

提供一些支持项目运行的函数,比如创建输出图像等等,具体的功能可以看程序。
程序如下:

import numpy as np
import cv2
from PIL import Image
from io import BytesIO, StringIO
import base64
import time# 定义一个函数,将遥测字符串转换为浮点数,与十进制约定无关
def convert_to_float(string_to_convert):if ',' in string_to_convert:float_value = np.float(string_to_convert.replace(',', '.'))else:float_value = np.float(string_to_convert)return float_valuedef update_rover(Rover, data):# 初始化开始时间和样本位置if Rover.start_time is None:Rover.start_time = time.time()Rover.total_time = 0samples_xpos = np.int_([convert_to_float(pos.strip()) for pos in data["samples_x"].split(';')])samples_ypos = np.int_([convert_to_float(pos.strip()) for pos in data["samples_y"].split(';')])Rover.samples_pos = (samples_xpos, samples_ypos)Rover.samples_to_find = np.int(data["sample_count"])# 或者只是更新已用时间eelse:tot_time = time.time() - Rover.start_timeif np.isfinite(tot_time):Rover.total_time = tot_time# 打印遥测数据字典中的字段print(data.keys())# 漫游者号的当前速度,单位为m / sRover.vel = convert_to_float(data["speed"])# 漫游者号的当前位置Rover.pos = [convert_to_float(pos.strip()) for pos in data["position"].split(';')]# 当前的漫游者号偏航角Rover.yaw = convert_to_float(data["yaw"])# 当前的漫游者号的倾斜度Rover.pitch = convert_to_float(data["pitch"])# 当前的漫游者号转动角度Rover.roll = convert_to_float(data["roll"])# 当前油门设置Rover.throttle = convert_to_float(data["throttle"])# 当前转向角Rover.steer = convert_to_float(data["steering_angle"])# Near sample 标志Rover.near_sample = np.int(data["near_sample"])# Picking up 标志Rover.picking_up = np.int(data["picking_up"])# 更新收集的岩石数量Rover.samples_collected = Rover.samples_to_find - np.int(data["sample_count"])print('speed =', Rover.vel, 'position =', Rover.pos, 'throttle =',Rover.throttle, 'steer_angle =', Rover.steer, 'near_sample:', Rover.near_sample,'picking_up:', data["picking_up"], 'sending pickup:', Rover.send_pickup,'total time:', Rover.total_time, 'samples remaining:', data["sample_count"],'samples collected:', Rover.samples_collected)# 从漫游者号的中央摄像头获取当前图像imgString = data["image"]image = Image.open(BytesIO(base64.b64decode(imgString)))Rover.img = np.asarray(image)# 返回更新的漫游者号和单独的图像以进行可选保存return Rover, image# 定义一个函数,根据世界地图结果创建显示输出
def create_output_images(Rover):# 创建一个缩放的地图,用于绘制和清理一下障碍物/导航像素if np.max(Rover.worldmap[:, :, 2]) > 0:nav_pix = Rover.worldmap[:, :, 2] > 0navigable = Rover.worldmap[:, :, 2] * (255 / np.mean(Rover.worldmap[nav_pix, 2]))else:navigable = Rover.worldmap[:, :, 2]if np.max(Rover.worldmap[:, :, 0]) > 0:obs_pix = Rover.worldmap[:, :, 0] > 0obstacle = Rover.worldmap[:, :, 0] * (255 / np.mean(Rover.worldmap[obs_pix, 0]))else:obstacle = Rover.worldmap[:, :, 0]likely_nav = navigable >= obstacleobstacle[likely_nav] = 0plotmap = np.zeros_like(Rover.worldmap)plotmap[:, :, 0] = obstacleplotmap[:, :, 2] = navigableplotmap = plotmap.clip(0, 255)# 覆盖障碍物和可导航的地形图与地面实况图map_add = cv2.addWeighted(plotmap, 1, Rover.ground_truth, 0.5, 0)# 检查worldmap中是否存在任何岩石rock_world_pos = Rover.worldmap[:, :, 1].nonzero()# 如果有,我们将逐步完成已知的样品位置# 确认检测是否真实samples_located = 0if rock_world_pos[0].any():rock_size = 2for idx in range(len(Rover.samples_pos[0])):test_rock_x = Rover.samples_pos[0][idx]test_rock_y = Rover.samples_pos[1][idx]rock_sample_dists = np.sqrt((test_rock_x - rock_world_pos[1]) ** 2 + \(test_rock_y - rock_world_pos[0]) ** 2)# 如果在已知样品位置3米范围内检测到岩石,# 则认为它成功并在地图上绘制已知样品的位置if np.min(rock_sample_dists) < 3:samples_located += 1map_add[test_rock_y - rock_size:test_rock_y + rock_size,test_rock_x - rock_size:test_rock_x + rock_size, :] = 255# 计算地图结果的一些统计数据# 首先获取可导航地形图中的总像素数tot_nav_pix = np.float(len((plotmap[:, :, 2].nonzero()[0])))# 接下来计算出其中有多少对应于地面实况像素good_nav_pix = np.float(len(((plotmap[:, :, 2] > 0) & (Rover.ground_truth[:, :, 1] > 0)).nonzero()[0]))# 接下来找出有多少不对应地面实况像素bad_nav_pix = np.float(len(((plotmap[:, :, 2] > 0) & (Rover.ground_truth[:, :, 1] == 0)).nonzero()[0]))# 抓取地图像素的总数tot_map_pix = np.float(len((Rover.ground_truth[:, :, 1].nonzero()[0])))# 计算已成功找到的地面实况图的百分比perc_mapped = round(100 * good_nav_pix / tot_map_pix, 1)# 计算好的地图像素检测数除以总像素数# 发现是可导航的地形if tot_nav_pix > 0:fidelity = round(100 * good_nav_pix / (tot_nav_pix), 1)else:fidelity = 0# 翻转地图进行绘图,使y轴在显示屏中指向上方map_add = np.flipud(map_add).astype(np.float32)# 添加一些关于地图和岩石样本检测结果的文字cv2.putText(map_add, "Time: " + str(np.round(Rover.total_time, 1)) + ' s', (0, 10),cv2.FONT_HERSHEY_COMPLEX, 0.4, (255, 255, 255), 1)cv2.putText(map_add, "Mapped: " + str(perc_mapped) + '%', (0, 25),cv2.FONT_HERSHEY_COMPLEX, 0.4, (255, 255, 255), 1)cv2.putText(map_add, "Fidelity: " + str(fidelity) + '%', (0, 40),cv2.FONT_HERSHEY_COMPLEX, 0.4, (255, 255, 255), 1)cv2.putText(map_add, "Rocks", (0, 55),cv2.FONT_HERSHEY_COMPLEX, 0.4, (255, 255, 255), 1)cv2.putText(map_add, "  Located: " + str(samples_located), (0, 70),cv2.FONT_HERSHEY_COMPLEX, 0.4, (255, 255, 255), 1)cv2.putText(map_add, "  Collected: " + str(Rover.samples_collected), (0, 85),cv2.FONT_HERSHEY_COMPLEX, 0.4, (255, 255, 255), 1)# 将地图和视觉图像转换为base64字符串以便发送到服务器pil_img = Image.fromarray(map_add.astype(np.uint8))buff = BytesIO()pil_img.save(buff, format="JPEG")encoded_string1 = base64.b64encode(buff.getvalue()).decode("utf-8")pil_img = Image.fromarray(Rover.vision_image.astype(np.uint8))buff = BytesIO()pil_img.save(buff, format="JPEG")encoded_string2 = base64.b64encode(buff.getvalue()).decode("utf-8")return encoded_string1, encoded_string2

(4)主函数drive_rover.py

控制漫游者号运行的主程序,调用前面三个子程序完成漫游者号的自动驾驶功能。

其中有一些关于socketio服务器和Flask应用程序的语句,这里不详细说明了,可能的话我以后会单独写一篇文章来单独说明。如果有想要详细的,可以看这里。

其程序如下:

import argparse
import shutil
import base64
from datetime import datetime
import os
import cv2
import numpy as np
import socketio
import eventlet
import eventlet.wsgi
from PIL import Image
from flask import Flask
from io import BytesIO, StringIO
import json
import pickle
import matplotlib.image as mpimg
import time# Import functions for perception and decision making
from perception import perception_step
from decision import decision_step
from supporting_functions import update_rover, create_output_images
# 初始化socketio服务器和Flask应用程序
# (learn more at: https://python-socketio.readthedocs.io/en/latest/)
sio = socketio.Server()
app = Flask(__name__)# 读取地面实况图并创建绿色通道的图像
# 注意:图像默认以左上角为原点(0,0)y轴向下读取图片,要求图片为单层
ground_truth = mpimg.imread('E:/2019/RoboND-Rover-Project-master/calibration_images/map_bw.png')
ground_truth = ground_truth[:,:,0]  # 我的图片维度出了一些问题
# 下一行在红色和蓝色通道中创建零数组,并将地图放入绿色通道。
# 这就是地图输出在显示图像中显示为绿色的原因
ground_truth_3d = np.dstack((ground_truth*0, ground_truth*255, ground_truth*0)).astype(np.float)# 定义RoverState()类
class RoverState():def __init__(self):self.start_time = None  # 记录导航开始的时间self.total_time = None  # 记录导航的总持续时间self.img = None  # 当前的相机图像self.pos = None  # 当前的位置 (x, y)self.yaw = None  # 当前的偏航角self.pitch = None  # 当前的俯仰角self.roll = None  # 当前的旋转角self.vel = None  # 当前的速度self.steer = 0  # 当前的转向角self.throttle = 0  # 当前的油门值self.brake = 0  # 当前的制动值self.nav_angles = None  # 可导航地形像素的角度self.nav_dists = None  # 可导航地形像素的距离self.ground_truth = ground_truth_3d  # 真实的世界地图self.mode = 'forward'  # 当前模式 (可以是前进或者停止)self.throttle_set = 0.2  # 加速时的节流设定self.brake_set = 10  # 刹车时的刹车设定# 下面的stop_forward和go_forward字段表示可导航地形像素的总数。# 这是一种非常粗糙的形式来表示漫游者号当何时可以继续前进或者何时应该停止。# 可以随意添加新字段或修改这些字段。self.stop_forward = 50  # T启动停止时的阈值self.go_forward = 500  # 再次前进的阈值self.max_vel = 2  # 最大速度 (m/s)# 感知步骤的图像输入# 更新此图像以显示中间分析步骤# 在自主模式下的屏幕上self.vision_image = np.zeros((160, 320, 3), dtype=np.float)# 世界地图# 使用可导航的位置更新此图像# 障碍物和岩石样本self.worldmap = np.zeros((200, 200, 3), dtype=np.float)self.samples_pos = None  # 存储实际样本的位置self.samples_to_find = 0  # 储存样本的初始计数self.samples_located = 0  # 储存位于地图上的样本数self.samples_collected = 0  # 储存收集的样本数self.near_sample = 0  # 设置附近样本数为0self.picking_up = 0  # 设置["picking_up"]值self.send_pickup = False# 实例化类
Rover = RoverState()# 用于跟踪每秒帧数的变量(FPS)
# 初始化帧计数器
frame_counter = 0
# 设置帧计数器
second_counter = time.time()
fps = None# 定义遥测功能
@sio.on('telemetry')
def telemetry(sid, data):global frame_counter, second_counter, fpsframe_counter += 1# 粗略计算每秒帧数(FPS)if (time.time() - second_counter) > 1:fps = frame_counterframe_counter = 0second_counter = time.time()print("Current FPS: {}".format(fps))if data:global Rover# 使用当前遥测技术初始化/更新漫游者号Rover, image = update_rover(Rover, data)if np.isfinite(Rover.vel):# 执行感知和决策步骤以更新漫游者号的状态Rover = perception_step(Rover)Rover = decision_step(Rover)# 创建输出图像以发送到服务器out_image_string1, out_image_string2 = create_output_images(Rover)# 行动步骤!,发送命令到漫游者号# 不要同时发送这两个,它们都会触发模拟器发送回新的遥测数据,# 因此我们只能将其中一个发送回当前的遥测数据。# 如果处于可拾取岩石的状态发送拾取命令if Rover.send_pickup and not Rover.picking_up:send_pickup()Rover.send_pickup = Falseelse:# 发送命令到漫游者号commands = (Rover.throttle, Rover.brake, Rover.steer)send_control(commands, out_image_string1, out_image_string2)# 如果遥测无效,请发送空命令else:# 发送零点用于油门,制动和转向以及清空图像send_control((0, 0, 0), '', '')# 如果要从自动驾驶中保存摄像机图像,请指定路径# Example: $ python drive_rover.py image_folder_path# 如果指定了文件夹,则可以保存图像帧if args.image_folder != '':timestamp = datetime.utcnow().strftime('%Y_%m_%d_%H_%M_%S_%f')[:-3]image_filename = os.path.join(args.image_folder, timestamp)image.save('{}.jpg'.format(image_filename))else:sio.emit('manual', data={}, skip_sid=True)@sio.on('connect')
def connect(sid, environ):print("connect ", sid)send_control((0, 0, 0), '', '')sample_data = {}sio.emit("get_samples",sample_data,skip_sid=True)def send_control(commands, image_string1, image_string2):# 定义要发送到漫游者号的命令data = {'throttle': commands[0].__str__(),'brake': commands[1].__str__(),'steering_angle': commands[2].__str__(),'inset_image1': image_string1,'inset_image2': image_string2,}# 通过socketIO服务器发送命令sio.emit("data",data,skip_sid=True)eventlet.sleep(0)# 定义一个发送“拾取”命令的功能
def send_pickup():print("Picking up")pickup = {}sio.emit("pickup",pickup,skip_sid=True)eventlet.sleep(0)if __name__ == '__main__':parser = argparse.ArgumentParser(description='Remote Driving')parser.add_argument('image_folder',type=str,nargs='?',default='',help='Path to image folder. This is where the images from the run will be saved.')args = parser.parse_args()# os.system('rm -rf IMG_stream/*')if args.image_folder != '':print("Creating image folder at {}".format(args.image_folder))if not os.path.exists(args.image_folder):os.makedirs(args.image_folder)else:shutil.rmtree(args.image_folder)os.makedirs(args.image_folder)print("Recording this run ...")else:print("NOT recording this run ...")# 用socketio的中间件包装Flask应用程序app = socketio.Middleware(sio, app)# 部署为eventlet WSGI服务器eventlet.wsgi.server(eventlet.listen(('', 4567)), app)

其输出如下:

gif演示:

因为图片大小限制的原因,gif的质量比较差。

但是我们可以看出,程序已经可以基本正常运行了。但是我们的自主驾驶还有很多问题,比如当我们正对着岩石障碍时,漫游者号会选择直冲向岩石。这是由我们取平均值的算法决定的,但是这完全是不可行的。

而且漫游者号现在还处于一个随机驾驶状态,它只能在地图中进行随机的驾驶,无法遍历整个地图。这也是我们的程序需要升级的地方。

在下个笔记中,要实现漫游者号对地图进行稳定的遍历,而且要对所有岩石样本进行采集。然后我们才算完成了这个漫游者号火星车项目的所有部分。

Udacity机器人软件工程师课程笔记(五)-样本搜索和找回-基于漫游者号模拟器-自主驾驶相关推荐

  1. Udacity机器人软件工程师课程笔记(六)-样本搜索和找回-基于漫游者号模拟器-优化和样本找回

    10.优化和样本找回 (1)优化概述 在之前的一篇笔记中,我们已经实现了基本的漫游者号的自主驾驶功能.但是因为我们的感知子函数和决策子函数都过于简单,使漫游者号不能很好的自动驾驶和样本找回. 这篇笔记 ...

  2. Udacity机器人软件工程师课程笔记(一)-样本搜索和找回-基于漫游者号模拟器

    Robotics Software engineer编程笔记(一) 使用Udacity提供的漫游者号模拟器创建环境地图,寻找样本. 该项目是根据美国国家航空航天局(NASA)的样本返回挑战进行建模的. ...

  3. Udacity机器人软件工程师课程笔记(三)-样本搜索和找回-基于漫游者号模拟器-使用moviepy输出测试视频

    6.方法测试 在这个部分我们要整体的测试我们的程序,对前面的知识和内容有一个整体的应用和概括. 这是Udacity提供的相应资料,在code文件夹中有一个Rover_Project_Test_Note ...

  4. Udacity机器人软件工程师课程笔记(四)-样本搜索和找回-基于漫游者号模拟器-决策树

    7.做出决策 我们已经建立了感知步骤Perception.py,接下来要构建决策函数decision.py . 我们利用一个基础的人工智能模型--决策树模型.其结构如下: 由于我们采用一个非常简单的模 ...

  5. Udacity机器人软件工程师课程笔记(二)-样本搜索和找回-基于漫游者号模拟器

    Robotics Software engineer编程笔记(二) 5.确定漫游者号的行进方向 (1)漫游者号如何确定自己的行进方向? 我们已经有了一个由前置摄像头得到的图像,然后可以通过对图像进行处 ...

  6. Udacity机器人软件工程师课程笔记(七)-ROS介绍和Turtlesim包的使用

    Robotics Software engineer笔记 1.ROS简介与虚拟机配置 (1)ROS简介 ROS是一款机器人软件框架,即机器人操作系统(Robot Operating System). ...

  7. Udacity机器人软件工程师课程笔记(三十五) - SLAM - 基于网格的FastSLAM

    一.SLAM介绍 即使定位和建图问题(simultaneous localization and mapping),一般简称为SLAM, 也称作(Concurrent Mapping and Loca ...

  8. Udacity机器人软件工程师课程笔记(十五)-运动学-正向运动学和反向运动学(其二)-DH参数等

    正向运动学和反向运动学 目录 2D中的旋转矩阵 sympy包 旋转的合成 旋转矩阵中的欧拉角 平移 齐次变换及其逆变换 齐次变换的合成 Denavit-Hartenberg 参数 DH参数分配算法 正 ...

  9. Udacity机器人软件工程师课程笔记(二十五) - 使用PID控制四轴飞行器 - 四轴飞行器(四旋翼)模拟器

    1.四轴飞行器运动学和动力学模型 在讨论四轴飞行器时,明确定义两个参考坐标系会很有帮助:一个固定的世界坐标系W{W}W和一个牢固地附着到四轴飞行器的质心(CoM)的运动坐标系B{B}B. 假设运动坐标 ...

最新文章

  1. 阿里京东带头打劫,下一个被干掉的就是你
  2. vue 初始化方法_Vue源码解读(一)引入Vue做了什么
  3. linux IP 命令使用举例
  4. 在VCS仿真器中使用FSDB
  5. springboot不会运行gc_SpringBoot 和JVM 调优(深度好文,建议收藏)
  6. c+智能指针源码分析_C ++中的智能指针
  7. java 写文件 并发_记录一次Java文件锁引起的并发写文件问题
  8. 51nod1417 天堂里的游戏
  9. c语言 程序停止,Go语言宕机(panic)——程序终止运行
  10. 创意网页排版设计和教程分享,打造 “视”不可挡的网页设计
  11. Webshell 管理工具
  12. 快速实现APP混合开发(Hybrid App开发)攻略
  13. java char a z_java中,char A,char a的值各是多少?
  14. python爬虫模拟登陆
  15. 2017国庆假期学习总结
  16. 时间序列分析-----1---简介
  17. 海思Hi3511芯片参数和工作原理介绍
  18. 校招生向京东发起的“攻势”,做到他这样,你,也可以
  19. C语言求二维数组鞍点
  20. 虚拟机VirtualBox启动虚拟机报Only Ethernet Adapter' (VERR_INTNET_FLT_IF_NOT_FOUND).

热门文章

  1. PYTHON自动化Day12-unittest自动注册登录
  2. kvm虚拟机vnc配置
  3. appium-DesiredCapability详解与实战
  4. Objective C内存管理之理解autorelease------面试题
  5. JavaSE replaceAll 方法
  6. 推荐一个HTML的语法高亮解析器
  7. visual studio配置第三方库
  8. usaco Ordered Fractions 顺序的分数(两种解法)
  9. java具有自动无用内存回收_Java语言程序设计(一)试卷及答案解释
  10. python slice是共享内存吗_在共享内存中使用numpy数组进行多处理