机器人操作系统ROS(九):MoveIt!与机械臂控制

  • MoveIt!简介
  • MoveIt!系统架构
  • MoveIt!编程与机械臂控制
    • 关节空间规划
    • 工作空间规划
    • 笛卡尔运动规划
    • 避障规划
  • Pick and Place示例

MoveIt!简介

MoveIt!是在2012年在PR2与多个机械臂功能包的基础上集成得到的ROS软件包,它为开发者提供了一个易于使用的集成化开发平台,由一系列移动操作的功能包组成,包括运动规划、操作控制、3D感知、运动学、控制与导航算法等,且提供友好的GUI,可以广泛应用于工业、商业、研发和其他领域。

使用MoveIt!实现机械臂控制的四个步骤:

  1. 组装:在控制之前需要有机器人,可以是真实的机械臂,也可以是仿真的机械臂,但都要创建完整的机器人URDF模型。
  2. 配置:使用MoveIt!控制机械臂之前,需要根据机器人的URDF模型,再使用Setup Assistant工具完成自碰撞矩阵、规划组、终端夹具等配置,配置完成后生成一个ROS功能包。
  3. 驱动:使用ArbotiX或者ros_control功能包中的控制器插件,实现对机械臂关节的驱动。插件的使用方法一般分为两步:首先创建插件的YAML配置文件,然后通过launch文件启动插件并加载配置参数。
  4. 控制:MoveIt!提供了C++、Python、rviz插件等接口,可以实现机器人关节空间和工作空间下的运动规划,规划过程中会综合考虑场景信息,并实现自主避障的优化控制。

关于MoveIt!安装与使用的更多内容可见教程。

MoveIt!系统架构

move_group是MoveIt!的核心节点,可以综合其他独立的功能组件作为用户提供ROS中的动作指令和服务。move_group本身并不具备丰富的功能,主要完成各功能包、插件的集成。它通过话题(Topic)的方式接收机器人发布的传感器信息、关节状态信息,以及机器人的TF坐标变换;它通过服务(Service)的方式运动规划器和运动学求解器通信;它还通过动作(Action)的方式向机器人控制器发送规划结果,并接受机器人执行情况的反馈;另外,还需要ROS参数服务器提供机器人的运动学参数,这些参数可根据机器人的URDF模型生成(SRDF和配置文件)。

MoveIt!编程与机械臂控制

MoveIt!的move_group提供了丰富的C++和Python的编程API,可以帮助我们完成更多运动控制的相关功能,接下来主要以Python API为例,介绍MoveIt!的编程方法。

关节空间规划

关节空间运动是以关节角度为控制量的机器人运动。机械臂关节空间的规划不需要考虑机器人终端的姿态。
使用如下命令实现MArm关节空间下的运动测试:

roslaunch marm_planning arm_planning.launch
rosrun marm_planning moveit_fk_demo.py

marm_planning/scripts/moveit_fk_demo.py的源码如下:

import rospy, sys
import moveit_commanderclass MoveItFkDemo:def __init__(self):# 初始化move_group的APImoveit_commander.roscpp_initialize(sys.argv)# 初始化ROS节点rospy.init_node('moveit_fk_demo', anonymous=True)# 初始化需要使用move group控制的机械臂中的arm grouparm = moveit_commander.MoveGroupCommander('manipulator')# 设置机械臂运动的允许误差值arm.set_goal_joint_tolerance(0.001)# 设置允许的最大速度和加速度arm.set_max_acceleration_scaling_factor(0.5)arm.set_max_velocity_scaling_factor(0.5)# 控制机械臂先回到初始化位置arm.set_named_target('home')arm.go()rospy.sleep(1)# 设置机械臂的目标位置,使用六轴的位置数据进行描述(单位:弧度)joint_positions = [0.391410, -0.676384, -0.376217, 0.0, 1.052834, 0.454125]arm.set_joint_value_target(joint_positions)# 控制机械臂完成运动arm.go()rospy.sleep(1)# 控制机械臂先回到初始化位置arm.set_named_target('home')arm.go()rospy.sleep(1)# 关闭并退出moveitmoveit_commander.roscpp_shutdown()moveit_commander.os._exit(0)if __name__ == "__main__":try:MoveItFkDemo()except rospy.ROSInterruptException:pass

工作空间规划

工作空间规划是通过机器人终端的三维坐标位置和姿态给定,在运动规划时使用逆向运动学求解各轴位置。
使用如下命令实现MArm工作空间下的运动测试:

roslaunch marm_planning arm_planning.launch
rosrun marm_planning moveit_ik_demo.py

marm_planning/scripts/moveit_ik_demo.py的源码如下:

import rospy, sys
import moveit_commander
from geometry_msgs.msg import PoseStamped, Poseclass MoveItIkDemo:def __init__(self):# 初始化move_group的APImoveit_commander.roscpp_initialize(sys.argv)# 初始化ROS节点rospy.init_node('moveit_ik_demo')# 初始化需要使用move group控制的机械臂中的arm grouparm = moveit_commander.MoveGroupCommander('manipulator')# 获取终端link的名称end_effector_link = arm.get_end_effector_link()# 设置目标位置所使用的参考坐标系reference_frame = 'base_link'arm.set_pose_reference_frame(reference_frame)# 当运动规划失败后,允许重新规划arm.allow_replanning(True)# 设置位置(单位:米)和姿态(单位:弧度)的允许误差arm.set_goal_position_tolerance(0.001)arm.set_goal_orientation_tolerance(0.001)# 设置允许的最大速度和加速度arm.set_max_acceleration_scaling_factor(0.5)arm.set_max_velocity_scaling_factor(0.5)# 控制机械臂先回到初始化位置arm.set_named_target('home')arm.go()rospy.sleep(1)# 设置机械臂工作空间中的目标位姿,位置使用x、y、z坐标描述,# 姿态使用四元数描述,基于base_link坐标系target_pose = PoseStamped()target_pose.header.frame_id = reference_frametarget_pose.header.stamp = rospy.Time.now()     target_pose.pose.position.x = 0.2593target_pose.pose.position.y = 0.0636target_pose.pose.position.z = 0.1787target_pose.pose.orientation.x = 0.911822target_pose.pose.orientation.y = -0.0269758target_pose.pose.orientation.z = 0.285694target_pose.pose.orientation.w = -0.293653# 设置机器臂当前的状态作为运动初始状态arm.set_start_state_to_current_state()# 设置机械臂终端运动的目标位姿arm.set_pose_target(target_pose, end_effector_link)# 规划运动路径traj = arm.plan()# 按照规划的运动路径控制机械臂运动arm.execute(traj)rospy.sleep(1)# 控制机械臂终端向右移动5cmarm.shift_pose_target(1, -0.05, end_effector_link)arm.go()rospy.sleep(1)# 控制机械臂终端反向旋转90度arm.shift_pose_target(3, -1.57, end_effector_link)arm.go()rospy.sleep(1)# 控制机械臂回到初始化位置arm.set_named_target('home')arm.go()# 关闭并退出moveitmoveit_commander.roscpp_shutdown()moveit_commander.os._exit(0)if __name__ == "__main__":MoveItIkDemo()

笛卡尔运动规划

工作空间的运动规划并没有对机器人终端轨迹有任何约束,目标位姿给定后,可以通过运动学反解获得关节空间下的各轴位置(即角度),接下来的规划和运动仍然在关节空间下完成。笛卡尔运动规划不仅考虑机械臂的起始位姿,还考虑了运动过程中的位姿约束。
使用如下命令实现MArm笛卡尔运动规划测试:

roslaunch marm_planning arm_planning_with_trail.launch
# 带路径约束的运动规划
rosrun marm_planning moveit_cartesian_demo.py _cartesian:=True
# 不带路径约束的运动规划
rosrun marm_planning moveit_cartesian_demo.py _cartesian:=False

marm_planning/scripts/moveit_cartesian_demo.py的源码如下:

import rospy, sys
import moveit_commander
from moveit_commander import MoveGroupCommander
from geometry_msgs.msg import Pose
from copy import deepcopyclass MoveItCartesianDemo:def __init__(self):# 初始化move_group的APImoveit_commander.roscpp_initialize(sys.argv)# 初始化ROS节点rospy.init_node('moveit_cartesian_demo', anonymous=True)# 初始化需要使用move group控制的机械臂中的arm grouparm = MoveGroupCommander('manipulator')# 当运动规划失败后,允许重新规划arm.allow_replanning(True)# 设置目标位置所使用的参考坐标系arm.set_pose_reference_frame('base_link')# 设置位置(单位:米)和姿态(单位:弧度)的允许误差arm.set_goal_position_tolerance(0.001)arm.set_goal_orientation_tolerance(0.001)# 设置允许的最大速度和加速度arm.set_max_acceleration_scaling_factor(0.5)arm.set_max_velocity_scaling_factor(0.5)# 获取终端link的名称end_effector_link = arm.get_end_effector_link()# 控制机械臂先回到初始化位置arm.set_named_target('home')arm.go()rospy.sleep(1)# 获取当前位姿数据最为机械臂运动的起始位姿start_pose = arm.get_current_pose(end_effector_link).poseprint start_pose# 初始化路点列表waypoints = []# 将初始位姿加入路点列表waypoints.append(start_pose)# 设置路点数据,并加入路点列表wpose = deepcopy(start_pose)wpose.position.z -= 0.2waypoints.append(deepcopy(wpose))wpose.position.x += 0.1waypoints.append(deepcopy(wpose))wpose.position.y += 0.1waypoints.append(deepcopy(wpose))fraction = 0.0   #路径规划覆盖率maxtries = 100   #最大尝试规划次数attempts = 0     #已经尝试规划次数# 设置机器臂当前的状态作为运动初始状态arm.set_start_state_to_current_state()# 尝试规划一条笛卡尔空间下的路径,依次通过所有路点while fraction < 1.0 and attempts < maxtries:(plan, fraction) = arm.compute_cartesian_path (waypoints,   # waypoint poses,路点列表0.01,        # eef_step,终端步进值0.0,         # jump_threshold,跳跃阈值True)        # avoid_collisions,避障规划# 尝试次数累加attempts += 1# 打印运动规划进程if attempts % 10 == 0:rospy.loginfo("Still trying after " + str(attempts) + " attempts...")# 如果路径规划成功(覆盖率100%),则开始控制机械臂运动if fraction == 1.0:rospy.loginfo("Path computed successfully. Moving the arm.")arm.execute(plan)rospy.loginfo("Path execution complete.")# 如果路径规划失败,则打印失败信息else:rospy.loginfo("Path planning failed with only " + str(fraction) + " success after " + str(maxtries) + " attempts.")  rospy.sleep(1)# 控制机械臂先回到初始化位置arm.set_named_target('home')arm.go()rospy.sleep(1)# 关闭并退出moveitmoveit_commander.roscpp_shutdown()moveit_commander.os._exit(0)if __name__ == "__main__":try:MoveItCartesianDemo()except rospy.ROSInterruptException:pass

避障规划

机器人在工作空间内运动规划过程中存在障碍物时,需要进行避障规划。

使用如下命令实现MArm避障规划测试:

roslaunch marm_planning arm_planning.launch
rosrun marm_planning moveit_obstacles_demo.py

marm_planning/scripts/moveit_obstacles_demo.py的源码如下:

import rospy, sys
import moveit_commander
from moveit_commander import MoveGroupCommander, PlanningSceneInterface
from moveit_msgs.msg import  PlanningScene, ObjectColor
from geometry_msgs.msg import PoseStamped, Poseclass MoveItObstaclesDemo:def __init__(self):# 初始化move_group的APImoveit_commander.roscpp_initialize(sys.argv)# 初始化ROS节点rospy.init_node('moveit_obstacles_demo')# 初始化场景对象scene = PlanningSceneInterface()# 创建一个发布场景变化信息的发布者self.scene_pub = rospy.Publisher('planning_scene', PlanningScene, queue_size=5)# 创建一个存储物体颜色的字典对象self.colors = dict()# 等待场景准备就绪rospy.sleep(1)# 初始化需要使用move group控制的机械臂中的arm grouparm = MoveGroupCommander('arm')# 获取终端link的名称end_effector_link = arm.get_end_effector_link()# 设置位置(单位:米)和姿态(单位:弧度)的允许误差arm.set_goal_position_tolerance(0.01)arm.set_goal_orientation_tolerance(0.05)# 当运动规划失败后,允许重新规划arm.allow_replanning(True)# 设置目标位置所使用的参考坐标系reference_frame = 'base_link'arm.set_pose_reference_frame(reference_frame)# 设置每次运动规划的时间限制:5sarm.set_planning_time(5)# 设置场景物体的名称table_id = 'table'box1_id = 'box1'box2_id = 'box2'# 移除场景中之前运行残留的物体scene.remove_world_object(table_id)scene.remove_world_object(box1_id)scene.remove_world_object(box2_id)    rospy.sleep(1)# 控制机械臂先回到初始化位置arm.set_named_target('home')arm.go()rospy.sleep(2)# 设置桌面的高度table_ground = 0.25# 设置table、box1和box2的三维尺寸table_size = [0.2, 0.7, 0.01]box1_size = [0.1, 0.05, 0.05]box2_size = [0.05, 0.05, 0.15]# 将三个物体加入场景当中table_pose = PoseStamped()table_pose.header.frame_id = reference_frametable_pose.pose.position.x = 0.26table_pose.pose.position.y = 0.0table_pose.pose.position.z = table_ground + table_size[2] / 2.0table_pose.pose.orientation.w = 1.0scene.add_box(table_id, table_pose, table_size)box1_pose = PoseStamped()box1_pose.header.frame_id = reference_framebox1_pose.pose.position.x = 0.21box1_pose.pose.position.y = -0.1box1_pose.pose.position.z = table_ground + table_size[2] + box1_size[2] / 2.0box1_pose.pose.orientation.w = 1.0   scene.add_box(box1_id, box1_pose, box1_size)box2_pose = PoseStamped()box2_pose.header.frame_id = reference_framebox2_pose.pose.position.x = 0.19box2_pose.pose.position.y = 0.15box2_pose.pose.position.z = table_ground + table_size[2] + box2_size[2] / 2.0box2_pose.pose.orientation.w = 1.0   scene.add_box(box2_id, box2_pose, box2_size)# 将桌子设置成红色,两个box设置成橙色self.setColor(table_id, 0.8, 0, 0, 1.0)self.setColor(box1_id, 0.8, 0.4, 0, 1.0)self.setColor(box2_id, 0.8, 0.4, 0, 1.0)# 将场景中的颜色设置发布self.sendColors()    # 设置机械臂的运动目标位置,位于桌面之上两个盒子之间target_pose = PoseStamped()target_pose.header.frame_id = reference_frametarget_pose.pose.position.x = 0.2target_pose.pose.position.y = 0.0target_pose.pose.position.z = table_pose.pose.position.z + table_size[2] + 0.05target_pose.pose.orientation.w = 1.0# 控制机械臂运动到目标位置arm.set_pose_target(target_pose, end_effector_link)arm.go()rospy.sleep(2)# 设置机械臂的运动目标位置,进行避障规划target_pose2 = PoseStamped()target_pose2.header.frame_id = reference_frametarget_pose2.pose.position.x = 0.2target_pose2.pose.position.y = -0.25target_pose2.pose.position.z = table_pose.pose.position.z + table_size[2] + 0.05target_pose2.pose.orientation.w = 1.0# 控制机械臂运动到目标位置arm.set_pose_target(target_pose2, end_effector_link)arm.go()rospy.sleep(2)# 控制机械臂回到初始化位置arm.set_named_target('home')arm.go()# 关闭并退出moveitmoveit_commander.roscpp_shutdown()moveit_commander.os._exit(0)# 设置场景物体的颜色def setColor(self, name, r, g, b, a = 0.9):# 初始化moveit颜色对象color = ObjectColor()# 设置颜色值color.id = namecolor.color.r = rcolor.color.g = gcolor.color.b = bcolor.color.a = a# 更新颜色字典self.colors[name] = color# 将颜色设置发送并应用到moveit场景当中def sendColors(self):# 初始化规划场景对象p = PlanningScene()# 需要设置规划场景是否有差异     p.is_diff = True# 从颜色字典中取出颜色设置for color in self.colors.values():p.object_colors.append(color)# 发布场景物体颜色设置self.scene_pub.publish(p)if __name__ == "__main__":try:MoveItObstaclesDemo()except KeyboardInterrupt:raise

Pick and Place示例

此节演示了一个简单的机器人pick and place应用,这个应用就是让机器人用夹爪将工作空间内的某个物体夹起来,然后将该物体放到目标位置。类似的功能在机器人的实际生产应用中使用非常广泛,例如码垛、搬运、挑拣等工作。

在这个应用例程中,假设已知物体的位置,MoveIt!需要控制机器人去抓取物体并放置到指定位置。

使用如下命令实现pick and place例程:

roslaunch marm_planning arm_planning.launch
rosrun marm_planning moveit_pick_and_place_demo.py

创建抓取的目标物体
创建一个用来抓取的目标物体,并将其设置颜色,代码如下:

target_size = [0.02,0.01,0.12]
target_pose = PoseStamped()
target_pose.header.frame_id = REFERENCE_FRAME
target_pose.pose.position.x = 0.32
target_pose.pose.position.y = 0.0
target_pose.pose.position.z = table_ground + table_size[2] + target_size[2]/2.0
target_pose.pose.orientation.w =1.0scene.add_box(target_id, target_pose, target_size)self.setColor(target_id, 0.9, 0.9, 0, 1.0)

设置目标物体的放置位置
创建一个place,并准备放置目标物体的位置,代码如下:

place_pose = PoseStamped()
place_pose.header.frame_id = REFERENCE_FRAME
place_pose.pose.position.x = 0.32
place_pose.pose.position.y = -0.2
place_pose.pose.position.z = table_ground + table_size[2] + target_size[2]/2.0
place_pose.pose.orientation.w = 1.0

生成抓取姿态
生成抓取姿态的代码如下:

grasp_pose = target_pose
grasps = self.make_grasps(grasp_pose, [target_id])
for grasp in grasps: self.gripper_pose_pub.publish(grasp.grasp_pose)rospy.sleep(0.2)

在抓去之前,需要确认目标位姿是否存在正确的抓取姿态。在pick时,目标物体的抓取位置就是物体摆放的位置,然后使用make_grasps()函数生成抓取姿态的列表,并将抓取姿态的消息发布,显示在rviz中,代码如下:

def make_grasps(self, initial_pose_stamped, allowed_touch_objects):# 初始化抓取姿态对象 g = Grasp()# 创建夹爪张开、闭合的姿态 g.pre_grasp_posture = self.make_gripper_posture(GRIPPER_OPEN)g.grasp_posture = self.make_gripper_posture(GRIPPER_CLOSED)# 设置期望的夹爪靠近、撤离目标的参数g.pre_grasp_approach = self.make_gripper_translation(0.01, 0.1, [1.0, 0.0, 0.0])g.post_grasp_retreat = self.make_gripper_translation(0.1, 0.15, [0.0, -1.0, 1.0])# 设置抓取姿态g.grasp_pose = initial_pose_stamped # 需要尝试改变姿态的数据列表pitch_vals = [0, 0.1, -0.1, 0.2, -0.2, 0.3, -0.3]yaw_vals = [0]# 抓取姿态的列表grasps = []# 改变姿态,生成抓取动作 for y in yaw_vals:for p in pitch_vals:# 欧拉角到四元数的转换q = quaternion _ from _ euler (0, p , y )# 设置抓取的姿态g.grasp_pose.pose.orientation.x = q[0]g.grasp_pose.pose.orientation.y = q[1]g.grasp_pose.pose.orientation.z = q[2]g.grasp_pose.pose.orientation.w = q[3]# 设置抓取的唯一 id 号g.id = str(len(grasps))# 设置允许接触的物体g.allowed_touch_objects = allowed_touch_objects# 将本次规划的抓取放入抓取列表中grasps.append(deepcopy(g))# 返回抓取列表return grasps

make_grasps()函数通过pitch角度的变化得到不同的抓取姿态。
pick
设置pick时的代码如下:

while result != MoveItErrorCodes.SUCCESS and n_attempts < max_pick_attempts:n_attempts += 1rospy.loginfo("Pick attempt: " + str(n_attempts))result = arm.pick(target_id, grasps)rospy.sleep(0.2)

接下来机器人就可以尝试 pick 目标物体了。针对不同的抓取姿态,如果无法求解运动学逆解或者规划轨迹会发生碰撞,pick 的运动规划就会出错,因此例程设置重新尝试规划的次数。如果规划成功,则 pick()会控制机器人按照规划轨迹运动。

place
如果 pick 阶段的运动控制没有问题,那我们的工作已经完成了一半。接下来的任务是 place ,将目标物体放置到指定位置,代码如下:

if result == MoveItErrorCodes.SUCCESS:result = Nonen_attempts = 0# 生成放置姿态 places = self.make_places(place_pose)# 重复尝试放置,直到成功或者最多尝试次数while result != MoveItErrorCodes.SUCCESS and n _ attempts < max_place_attempts:n _ attempts += 1rospy.loginfo("Placeattempt: " + str(n_attempts))for place in places:result = arm.place(target_id, place)if result == MoveItErrorCodes.SUCCESS:breakrospy.sleep(0.2)

place 与 pick 的原理是一致的,同样需要使用 make_places() 生成一个可能的夹爪放置姿态列表。然后根据这些可能的放置姿态尝试规划 place 操作的轨迹,规划成功后就可以控制机器人运动了,代码如下:

def make_places(self, init_pose)# 初始化放置抓取物体的位置place = PoseStamped()# 设置放置抓取物体的位置place = init_pose# 定义 x 方向上用于尝试放置物体的偏移参数x_vals = [0, 0.005, 0.01, 0.015, -0.005, -0.01, -0.0151]# 定义 y 方向上用于尝试放置物体的偏移参数y_vals = [0, 0.005, 0.01, 0.015, -0.005, -0.01, -0.015]pitch_vals = [0]# 定义用于尝试放置物体的偏航角参数yaw_vals = [0]# 定义放置物体的姿态列表places = []# 生成每个角度和偏移方向上的抓取姿态for y in yaw_vals:for p in pitch_vals:for y in y_vals:for x in x_vals:place.pose.position.x = init_pose.pose.position.x + xplace.pose.position.y = init_pose.pose.position.y + y# 欧拉角到四元数的转换q = quaternion_from_euler(0, p, y)# 欧拉角到四元数的转换place.pose.orientation.x = q[0] place.pose.orientation.y = q[1] place.pose.orientation.z = q[2] place.pose.orientation.w = q[3]# 将该放置姿态加入列表places.append(deepcopy(place))# 返回放置物体的姿态列表return places

make_places() 和 make_grasps() 的实现原理相同,都是通过设定的方向偏移、旋转列表生成多个可能的终端姿态

通过以上例程,我们就实现了一个简单的 pick and place 应用。

机器人操作系统ROS(九):MoveIt!与机械臂控制相关推荐

  1. ros kinetic-moveit驱动ur3机械臂------控制真实机械臂并且能动

    ros kinetic-moveit驱动ur3机械臂------控制真实机械臂并且能动 本文工作环境配置: ubuntu16.04.6 ros-kinetic ur3 已验证本教程代码在Ubuntu1 ...

  2. 机器人操作系统ROS(8)arbotix控制器控制小车运动

    本文内容:在 Ubuntu 18.04 虚拟机中,基于安装配置好 ROS Ketinc上,进行 URDF 机器人建模的详细步,同时配置摄像. 说明:本文是之前的延伸,该节需要参考上节机器人操作系统RO ...

  3. 六轴机械臂控制原理图_机械臂——六轴机械臂操作空间运动分析

    机械臂建模分析:https://blog.csdn.net/Kalenee/article/details/81990130 MoveIt规划下的关节空间运动分析:http://www.guyueho ...

  4. 【机器人操作系统(ROS)中的机械臂仿真】

    [机器人操作系统(ROS)中的机械臂仿真] 1. 前言 2. 什么是机械臂? 3. 设计机械臂 4. 模型设计 5. 了解启动文件 6. 了解自定义节点 7. 运行机械臂模拟 8. 结果和结论 1. ...

  5. ROS中阶笔记(九):Movelt!机械臂控制

    ROS中阶笔记(九):Movelt!机械臂控制 文章目录 1 Movelt!系统架构 1.1 Movelt!是什么 1.2 系统架构 1.3 Movelt!使用步骤 2 创建机械臂模型与配置文件 2. ...

  6. ROS Melodic连接UR5机械臂通讯详细步骤,亲测可行

    ros版本:Melodic 机械臂:优傲机器人ur5,控制器cb3,软件版本13.多 任务描述:机器人操作系统ros通过网线连接ur5,实现二者通信,在ros下控制ur5运动,为后续基于ros开发机械 ...

  7. 机器人操作系统ROS(1)

    ROS总体设计 ROS五个特点 点对点设计 一个使用ROS的系统包括一系列进程,这些进程存在于多个不同的主机并且在运行过程中通过端对端的拓扑结构进行联系,如图2所示.虽然基于中心服务器的那些软件框架也 ...

  8. 从零开始的ROS四轴机械臂控制-目录

    [从零开始的ROS四轴机械臂控制](一)- 实际模型制作.Solidworks文件转urdf与rviz仿真 一.模型制作 1.实际模型制作 2.Solidworks模型制作 二.Solidworks文 ...

  9. 【从零开始的ROS四轴机械臂控制】(二) - ROS与Gazebo连接,Gazebo仿真及urdf文件修改

    [从零开始的ROS四轴机械臂控制(二)] 四.urdf文件及gazebo仿真 1.simple_arm示例 (1)config文件夹 (2)launch文件夹 (3)meshes文件夹 (4)urdf ...

  10. 基于ROS设计一款机械臂控制系统 [转发]

    ROS探索总结-66.基于ROS设计一款机械臂控制系统 ROS探索总结-66.基于ROS设计一款机械臂控制系统 说明: 介绍如何基于ROS设计一款机械臂控制系统 正文 今天我们将从以下两个方面为大家介 ...

最新文章

  1. NVIDIA CUDA-X AI
  2. (转)新开发Apple Store上软件的实施步骤
  3. TLS就是SSL的升级版+网络安全——一图看懂HTTPS建立过程——本质上就是引入第三方监管,web服务器需要先生成公钥和私钥,去CA申请,https通信时候浏览器会去CA校验CA证书的有效性...
  4. 深圳市城镇职工社会医疗保险现金报销管理办法(二)
  5. 【Linux】24_网络管理数据链路层详解
  6. Python爬虫开发:post请求(用户登录)
  7. NOIP2021:游记
  8. Python 协程gevent
  9. HDU1253 胜利大逃亡
  10. context.Request.Files超过了最大请求长度
  11. HTML5 Canvas中处理图像和视频
  12. java编码转换报错_Java中BeanUtils的日期转换 代码报错 怎么解决
  13. vue ---- 数组的常用方法
  14. docker-compose的一些理解
  15. window.postMessage
  16. ABP vnext框架 返回JSON时间带T格式转换解决方案
  17. 逻辑回归-逐步回归(stepwise regression)的一些思考
  18. 外星人 Alienware X14 评测
  19. 触摸世界:10年前我写下这些诗歌,10年后我也不讨厌它们
  20. 一、14.猜码游戏:每一轮里,程序随机生成两个数字,一个是码数,0到5,一个是猜数,码数到10。用户也输入码数和猜数。若这一轮程序的猜数等于两个码数之和,输出“电脑胜”,若都没猜对或都猜对了,公布双方

热门文章

  1. 数学建模指标合成客观权重法matlab实现
  2. 毛书卿:今日黄金原油走势操作分析,白银TD走势短线操作建议
  3. django项目实战基于Python实现的飞机票销售系统
  4. 基于Face++网络爬虫+人脸融合算法智能发型推荐程序——深度学习算法应用(含Python及打包exe工程源码)+爬虫数据集
  5. c++语言程序设计考试试题,电大C++语言程序设计期末考试试题及答案小抄参考
  6. 微信小程序-阅读小程序demo
  7. iOS-控制系统音量与屏幕亮度
  8. 测试打印机使用端口方式
  9. SQL常规操作(看完基本会sql大部分操作了)
  10. Java实现S-DES加密算法