Mujoco 机械臂 OMPL 进行 RRT 关节空间路径规划避障、绕障

视频讲解:Mujoco 机械臂 OMPL 进行 RRT 关节空间路径规划避障、绕障_哔哩哔哩_bilibili

代码仓库:https://github.com/LitchiCheng/mujoco-learning

大多数场景下,机械臂的应用一般分为路径规划、轨迹优化、或者运动学 IK,FK,动力学等等,但机械臂仅仅只考虑理想路径的话往往在实际场景中是不够的,在路径规划前静态障碍物、轨迹跟随中动态障碍物,这些都是实际需要考虑的因素,接下来我们来看下同通过关节空间考虑静态障碍物的路径规划,使用 OMPL 库进行规划,关节起始和目标点使用 IK 方式生成。

设置静态障碍物的方式可以添加字段到 xml 中,

复制代码
 <geom name="obstacle_0" type="box" size="0.070 0.070 0.070" pos="0.250 0.220 0.500" contype="1" conaffinity="1" mass="0.0" rgba="0.400 0.300 0.300 0.800" />

也可以用封装好的方式,直接设置

复制代码
self.addObstacles(self.obstacles_pos, self.obstacles_type, self.obstacles_size, self.obstacles_rgba)

生成起始位置和目标位置生成的方式走 IK:

复制代码
    def getBipolarJoints(self):
        self.initial_pos = self.model.key_qpos[0]  
        print("start dof", self.initial_pos)
        for i in range(self.model.nq):
            self.data.qpos[i] = self.initial_pos[i]
        self.start_dof = self.data.qpos[:9].copy()

        euler = np.random.uniform(0, 2*np.pi, 3)
        tf = utils.transform2mat(self.goal_x, self.goal_y, self.goal_z, euler[0], euler[1], euler[2])
        self.solver = casadi_ik.Kinematics(self.ee_body_name)
        self.solver.buildFromMJCF(self.arm_path)
        self.dof, info = self.solver.ik(tf, current_arm_motor_q=self.start_dof)
        print("goal dof", self.dof)
        self.goal_dof = self.dof
        if len(self.goal_dof) < 9:
            self.goal_dof = np.concatenate((self.goal_dof, self.start_dof[7:]))

使用ompl进行关节空间(7x1)的路径规划,搜索路径的方法使用 RRT Connect(两头一起找),将障碍物碰撞数量作为状态是否有效的判断条件(ompl会自动考虑该条件):

复制代码
    def pathPlaning(self):
        state_space = ob.RealVectorStateSpace(self.model.nq)
        bounds = ob.RealVectorBounds(self.model.nq)
        for i in range(min(self.model.nq, self.model.jnt_range.shape[0])):
            bounds.setLow(i, self.model.jnt_range[i, 0])
            bounds.setHigh(i, self.model.jnt_range[i, 1])
        state_space.setBounds(bounds)
        si = ob.SpaceInformation(state_space)

        def is_state_valid(state):
            self.data.qpos[:7] = [state[i] for i in range(7)]
            mujoco.mj_step(self.model, self.data)
            return self.data.ncon == 0

        si.setStateValidityChecker(ob.StateValidityCheckerFn(is_state_valid))
        si.setup()
        start = ob.State(state_space)
        goal = ob.State(state_space)
        for i in range(min(self.model.nq, self.model.jnt_range.shape[0])):
            start[i] = self.start_dof[i]
            goal[i] = self.goal_dof[i]

        pdef = ob.ProblemDefinition(si)
        pdef.setStartAndGoalStates(start, goal)
        opt = ob.PathLengthOptimizationObjective(si)
        pdef.setOptimizationObjective(opt)
        planner = og.RRTConnect(si)
        self.planning_range = 0.01
        planner.setRange(self.planning_range)
        planner.setIntermediateStates(True)
        planner.setProblemDefinition(pdef)
        planner.setup()
        self.planning_timeout = 3.0
        solved = planner.solve(self.planning_timeout)
        self.path_states = []
        if solved:
            self.path = pdef.getSolutionPath()
            for i in range(self.path.getStateCount()):
                state = self.path.getState(i)
                state_values = [state[i] for i in range(self.model.nq)]
                self.path_states.append(state_values)
                # print(state_values)
        else:
            print("No solution found.")
        self.index = 0
        return solved

默认尝试10次搜索路径,如果都失败的话,需要去掉障碍物看下是否本身就是在可达空间内,完整代码如下:

复制代码
import mujoco
import ompl.base as ob
import ompl.geometric as og
import time
import mujoco_viewer
import src.casadi_ik as casadi_ik
import src.kdl_ik as kdl_ik
import src.key_listener as key_listener
import utils
import numpy as np
from pynput import keyboard

key_states = {
    keyboard.Key.down: False
}

class Test(mujoco_viewer.CustomViewer):
    def __init__(self, rendor_path, arm_path):
        super().__init__(rendor_path, 3, azimuth=-90, elevation=-30)
        self.arm_path = arm_path
        
        self.obstacles_size = []
        self.obstacles_pos = []
        self.obstacles_rgba = []
        self.obstacles_type = []

        self.obstacles_type.append("box")
        self.obstacles_size.append([0.07, 0.07, 0.07])
        self.obstacles_pos.append([0.25, 0.22, 0.5])
        self.obstacles_rgba.append([0.4, 0.3, 0.3, 0.8])

        # self.obstacles_type.append("sphere")
        # self.obstacles_size.append([0.06])
        # self.obstacles_pos.append([0.3, -0.01, 0.7])
        # self.obstacles_rgba.append([0.3, 0.3, 0.3, 0.8])
        self.addObstacles(self.obstacles_pos, self.obstacles_type, self.obstacles_size, self.obstacles_rgba)
        
        self.key_listener = key_listener.KeyListener(key_states)
        self.key_listener.start()
    
    def getBipolarJoints(self):
        self.initial_pos = self.model.key_qpos[0]  
        print("start dof", self.initial_pos)
        for i in range(self.model.nq):
            self.data.qpos[i] = self.initial_pos[i]
        self.start_dof = self.data.qpos[:9].copy()

        euler = np.random.uniform(0, 2*np.pi, 3)
        tf = utils.transform2mat(self.goal_x, self.goal_y, self.goal_z, euler[0], euler[1], euler[2])
        self.solver = casadi_ik.Kinematics(self.ee_body_name)
        self.solver.buildFromMJCF(self.arm_path)
        self.dof, info = self.solver.ik(tf, current_arm_motor_q=self.start_dof)
        print("goal dof", self.dof)
        self.goal_dof = self.dof
        if len(self.goal_dof) < 9:
            self.goal_dof = np.concatenate((self.goal_dof, self.start_dof[7:]))
        
    def pathPlaning(self):
        state_space = ob.RealVectorStateSpace(self.model.nq)
        bounds = ob.RealVectorBounds(self.model.nq)
        for i in range(min(self.model.nq, self.model.jnt_range.shape[0])):
            bounds.setLow(i, self.model.jnt_range[i, 0])
            bounds.setHigh(i, self.model.jnt_range[i, 1])
        state_space.setBounds(bounds)
        si = ob.SpaceInformation(state_space)

        def is_state_valid(state):
            self.data.qpos[:7] = [state[i] for i in range(7)]
            mujoco.mj_step(self.model, self.data)
            return self.data.ncon == 0

        si.setStateValidityChecker(ob.StateValidityCheckerFn(is_state_valid))
        si.setup()
        start = ob.State(state_space)
        goal = ob.State(state_space)
        for i in range(min(self.model.nq, self.model.jnt_range.shape[0])):
            start[i] = self.start_dof[i]
            goal[i] = self.goal_dof[i]

        pdef = ob.ProblemDefinition(si)
        pdef.setStartAndGoalStates(start, goal)
        opt = ob.PathLengthOptimizationObjective(si)
        pdef.setOptimizationObjective(opt)
        planner = og.RRTConnect(si)
        self.planning_range = 0.01
        planner.setRange(self.planning_range)
        planner.setIntermediateStates(True)
        planner.setProblemDefinition(pdef)
        planner.setup()
        self.planning_timeout = 3.0
        solved = planner.solve(self.planning_timeout)
        self.path_states = []
        if solved:
            self.path = pdef.getSolutionPath()
            for i in range(self.path.getStateCount()):
                state = self.path.getState(i)
                state_values = [state[i] for i in range(self.model.nq)]
                self.path_states.append(state_values)
                # print(state_values)
        else:
            print("No solution found.")
        self.index = 0
        return solved
    
    def createTask(self):
        try_cnt = 10
        self.success = False
        for i in range(try_cnt):
            self.getBipolarJoints()
            self.success = self.pathPlaning()
            if self.success:
                break
            print("Try again... cnt ", i)
    
    def runBefore(self):
        self.model.opt.timestep = 0.005
        self.ee_body_name = "ee_center_body"
        self.goal_x = 0.4
        self.goal_y = 0.3
        self.goal_z = 0.4
        self.usr_geom_size = []
        self.usr_geom_pos = []
        self.usr_geom_rgba = []
        self.usr_geom_type = []
        self.usr_geom_pos.append([self.goal_x, self.goal_y, self.goal_z])
        self.usr_geom_type.append("sphere")
        self.usr_geom_size.append([0.02])
        self.usr_geom_rgba.append([0.1, 0.3, 0.3, 0.8])
        self.addVisuGeom(self.usr_geom_pos, self.usr_geom_type, self.usr_geom_size, self.usr_geom_rgba)
        self.createTask()

    def runFunc(self):
        if not self.success:
            return 
        if(len(self.path_states) == 0):
            return
        if self.index < len(self.path_states):
            self.data.qpos[:7] = self.path_states[self.index][:7]
            self.index += 1
        else:
            self.data.qpos[:7] = self.path_states[-1][:7]
            if key_states[keyboard.Key.down]:
                print("re create task")
                self.index = 0
                self.createTask()
        
if __name__ == "__main__":
    SCENE_XML_PATH = 'model/franka_emika_panda/scene_pos.xml'
    ARM_XML_PATH = 'model/franka_emika_panda/panda_pos.xml'
    test = Test(SCENE_XML_PATH, ARM_XML_PATH)
    test.run_loop()

绑定了方向键down,可以重复规划,方便观察

相关推荐
烤麻辣烫41 分钟前
黑马程序员苍穹外卖(新手)DAY10
java·开发语言·学习·spring·intellij-idea
三年呀43 分钟前
深入探索量子机器学习:原理、实践与未来趋势的全景剖析
人工智能·深度学习·机器学习·量子计算
阿杰学AI43 分钟前
AI核心知识22——大语言模型之重要参数Top-P(简洁且通俗易懂版)
人工智能·ai·语言模型·aigc·模型参数·top-p
腾讯云开发者43 分钟前
架构火花|35岁程序员该做些什么:留在国企vs切换赛道
人工智能
Christo31 小时前
AAAI-2013《Spectral Rotation versus K-Means in Spectral Clustering》
人工智能·算法·机器学习·数据挖掘·kmeans
摘星编程1 小时前
openGauss DataVec向量数据库集成:面向AI应用的相似性搜索与知识图谱存储
数据库·人工智能·知识图谱
waves浪游1 小时前
进程控制(上)
linux·运维·服务器·开发语言·c++
qq_376766241 小时前
机房U位资产管理系统的数据分析能力:如何让数据中心运维效率升级?
网络·人工智能
程序员三明治1 小时前
【Java】synchronized关键字详解:从字节码到对象头与锁升级
java·开发语言·juc·synchronized··锁升级