- 2025-04-02
-
回复了主题帖:
MuJoCo 机械臂物体碰撞、接触检测方式一
蓝雨夜 发表于 2025-4-2 08:37
下次有没有安排从硬件慢慢搭建,选型,到调试的啊
随性做,哈哈哈,不是做课程
-
回复了主题帖:
MuJoCo 机械臂物体碰撞、接触检测方式一
Jacktang 发表于 2025-4-2 07:42
机械臂抓取物体的场景中,或训练的过程中,还需要示教器吗
现在都是用遥操
-
回复了主题帖:
《具身智能机器人系统》读后感,第三、四章
jobszheng5 发表于 2025-4-1 15:15
赞同楼主的说法——端测部署大模型的可能性不大。
我觉得端侧首先是要完成执行器的功能。控制 ...
是的,可以跑小模型
-
发表了主题帖:
MuJoCo 机械臂物体碰撞、接触检测方式一
视频讲解:MuJoCo 机械臂物体碰撞、接触检测方式一_哔哩哔哩_bilibili
仓库地址:GitHub - LitchiCheng/mujoco-learning
在机械臂抓取物体的场景中,或训练的过程中,较为重要的就是如何判断物体是否被抓住了,或者是否发生了碰撞。
但实际应用场景中,有很多方式可以判断,可以通过视觉识别,通过力传感器进行判断,那在MuJoCo中可以通过什么方式进行判断,今天分享一下其中的一种方式。
在 mjData.contact 数组,存储当前仿真步中所有活跃的接触点信息。每个接触点包含以下字段:
geom1 / geom2:接触的两个几何体的ID
dist:接触距离(负值表示穿透深度)
pos:接触点的三维位置坐标
frame:接触点的局部坐标系(3x3矩阵,法线方向为第一行)
friction:摩擦系数
solref / solimp:求解器参数
关键代码:
# 遍历所有接触点
for i in range(data.ncon):
contact = data.contact[i]
# 获取几何体对应的body_id
body1_id = model.geom_bodyid[contact.geom1]
body2_id = model.geom_bodyid[contact.geom2]
# 通过mj_id2name转换body_id为名称
body1_name = mujoco.mj_id2name(model, mujoco.mjtObj.mjOBJ_BODY, body1_id)
body2_name = mujoco.mj_id2name(model, mujoco.mjtObj.mjOBJ_BODY, body2_id)
print(f"接触点 {i}: 几何体 {contact.geom1} 名字 {body1_name} 和 {contact.geom2} 名字 {body2_name} 在位置 {contact.pos} 发生接触")
完整代码如下:
import mujoco
import numpy as np
import glfw
import mujoco.viewer
from scipy.optimize import minimize
import numpy as np
import pinocchio
from numpy.linalg import norm, solve
def inverse_kinematics(current_q, target_dir, target_pos):
urdf_filename = '/home/dar/MuJoCoBin/mujoco-learning/franka_panda_description/robots/panda_arm.urdf'
# 从 URDF 文件构建机器人模型
model = pinocchio.buildModelFromUrdf(urdf_filename)
# 为模型创建数据对象,用于存储计算过程中的中间结果
data = model.createData()
# 指定要控制的关节 ID
JOINT_ID = 7
# 定义期望的位姿,使用目标姿态的旋转矩阵和目标位置创建 SE3 对象
oMdes = pinocchio.SE3(target_dir, np.array(target_pos))
# 将当前关节角度赋值给变量 q,作为迭代的初始值
q = current_q
# 定义收敛阈值,当误差小于该值时认为算法收敛
eps = 1e-4
# 定义最大迭代次数,防止算法陷入无限循环
IT_MAX = 1000
# 定义积分步长,用于更新关节角度
DT = 1e-2
# 定义阻尼因子,用于避免矩阵奇异
damp = 1e-12
# 初始化迭代次数为 0
i = 0
while True:
# 进行正运动学计算,得到当前关节角度下机器人各关节的位置和姿态
pinocchio.forwardKinematics(model, data, q)
# 计算目标位姿到当前位姿之间的变换
iMd = data.oMi[JOINT_ID].actInv(oMdes)
# 通过李群对数映射将变换矩阵转换为 6 维误差向量(包含位置误差和方向误差),用于量化当前位姿与目标位姿的差异
err = pinocchio.log(iMd).vector
# 判断误差是否小于收敛阈值,如果是则认为算法收敛
if norm(err) < eps:
success = True
break
# 判断迭代次数是否超过最大迭代次数,如果是则认为算法未收敛
if i >= IT_MAX:
success = False
break
# 计算当前关节角度下的雅可比矩阵,关节速度与末端速度的映射关系
J = pinocchio.computeJointJacobian(model, data, q, JOINT_ID)
# 对雅可比矩阵进行变换,转换到李代数空间,以匹配误差向量的坐标系,同时取反以调整误差方向
J = -np.dot(pinocchio.Jlog6(iMd.inverse()), J)
# 使用阻尼最小二乘法求解关节速度
v = -J.T.dot(solve(J.dot(J.T) + damp * np.eye(6), err))
# 根据关节速度更新关节角度
q = pinocchio.integrate(model, q, v * DT)
# 每迭代 10 次打印一次当前的误差信息
if not i % 10:
print(f"{i}: error = {err.T}")
# 迭代次数加 1
i += 1
# 根据算法是否收敛输出相应的信息
if success:
print("Convergence achieved!")
else:
print("\n"
"Warning: the iterative algorithm has not reached convergence "
"to the desired precision")
# 打印最终的关节角度和误差向量
print(f"\nresult: {q.flatten().tolist()}")
print(f"\nfinal error: {err.T}")
# 返回最终的关节角度向量(以列表形式)
return q.flatten().tolist()
def limit_angle(angle):
while angle > np.pi:
angle -= 2 * np.pi
while angle < -np.pi:
angle += 2 * np.pi
return angle
model = mujoco.MjModel.from_xml_path('/home/dar/MuJoCoBin/mujoco_menagerie/franka_emika_panda/scene.xml')
data = mujoco.MjData(model)
class CustomViewer:
def __init__(self, model, data):
self.handle = mujoco.viewer.launch_passive(model, data)
self.pos = 0.0001
# 找到末端执行器的 body id
self.end_effector_id = mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_BODY, 'left_finger')
print(f"End effector ID: {self.end_effector_id}")
if self.end_effector_id == -1:
print("Warning: Could not find the end effector with the given name.")
# 初始关节角度
self.initial_q = data.qpos[:7].copy()
print(f"Initial joint positions: {self.initial_q}")
theta = np.pi
self.R_x = np.array([
[1, 0, 0],
[0, np.cos(theta), -np.sin(theta)],
[0, np.sin(theta), np.cos(theta)]
])
self.x = 0.3
self.new_q = self.initial_q
def is_running(self):
return self.handle.is_running()
def sync(self):
self.handle.sync()
@property
def cam(self):
return self.handle.cam
@property
def viewport(self):
return self.handle.viewport
def run_loop(self):
status = 0
while self.is_running():
mujoco.mj_forward(model, data)
if status == 0:
if self.x < 0.5:
self.x += 0.01
new_q = inverse_kinematics(self.initial_q, self.R_x, [self.x, 0.0, 0.28])
else:
status = 1
elif status == 1:
data.ctrl[7] = 10.0
data.qpos[:7] = new_q
# 遍历所有接触点
for i in range(data.ncon):
contact = data.contact[i]
# 获取几何体对应的body_id
body1_id = model.geom_bodyid[contact.geom1]
body2_id = model.geom_bodyid[contact.geom2]
# 通过mj_id2name转换body_id为名称
body1_name = mujoco.mj_id2name(model, mujoco.mjtObj.mjOBJ_BODY, body1_id)
body2_name = mujoco.mj_id2name(model, mujoco.mjtObj.mjOBJ_BODY, body2_id)
print(f"接触点 {i}: 几何体 {contact.geom1} 名字 {body1_name} 和 {contact.geom2} 名字 {body2_name} 在位置 {contact.pos} 发生接触")
mujoco.mj_step(model, data)
self.sync()
viewer = CustomViewer(model, data)
viewer.cam.distance = 3
viewer.cam.azimuth = 0
viewer.cam.elevation = -30
viewer.run_loop()
演示代码,关节之间的碰撞,以及关节碰到地上,以及夹爪碰到长方体上
在Viewer中按C键,会显示碰撞的刚体
-
发表了日志:
MuJoCo 机械臂物体碰撞、接触检测方式一
- 2025-04-01
-
发表了主题帖:
《具身智能机器人系统》读后感,第十到十三章
第 10 章:加速机器人计算
实时性难题:机器人计算要实现实时性不容易,以前主要因为算力需求高(像 SLAM 算法处理图像数据、矩阵计算量超大),加上算法碎片化,不同算法计算模式差异大,很难用单一硬件加速,导致机器人常卡顿。
模块加速方法:
定位模块:物流机器人定位算法碎片化,通过硬件定制加速,比如针对定位中 6 自由度估计设计加速器,提升计算效率。
规划模块:路径规划要找最优路径避障,传统方法耗时。现在用因子图拆分复杂问题,分步骤加速,提升规划效率和实时性。
控制模块:机器人形态复杂(如人形机器人自由度超 44 个),控制难度大。针对任务空间力矩控制模型设计专用加速器,应对高自由度带来的控制挑战。
硬件对比:不同硬件加速效果有差异,像 EDX-CAR 对比 CPU、GPU、DSP 等,展示了不同组合的加速比,为硬件选择提供参考。
第 11 章:算法安全性
深度学习隐患:深度神经网络不安全,训练数据来源不可控(比如经典数据集靠公开网络收集,难保证安全),测试数据分布可能与训练数据不一致,容易被攻击干扰。
大模型安全问题:
幻觉问题:大模型生成内容与事实或用户指令不符,分 “事实性幻觉”(如错误回答登月第一人)和 “忠实性幻觉”(内容与上下文不一致),主要因数据缺陷,过度依赖训练数据模式导致。
具身智能安全:大模型用于机器人时,安全隐患更具体。需从三层面考量:长序列任务安全(拒绝执行有害任务)、子任务安全(避免危险组合)、执行过程安全,需要系统设计者和模型开发者协同限制。
第 12 章:系统可靠性
可靠性定义:可靠性不是安全性,指系统在规定条件和时间内正常运行的能力,关注容错、冗余、恢复能力。机器人可能遇温度异常、碰撞、断电等,设计可靠系统很重要。
机器人本体:运行在不稳定环境时,机械结构、传感器等可能出问题,比如硬件故障、部件磨损。
计算系统:软件错误、算法漏洞等,导致系统失常。已知的可靠设计常伴随冗余,增加面积、功耗负担,需平衡设计。
设计挑战:如何在不引入过多负担的前提下,为机器人计算系统设计可靠性保护,确保面对硬件故障、软件错误时仍能稳定工作,是关键研究方向。
可靠性漏洞:机器人系统可靠性关注系统在规定条件下稳定运行的能力。机器人本体可能遇硬件故障、环境干扰,计算系统也会有软件错误、算法漏洞。以自动驾驶软件 Autoware 为例,算法错误类 Bug 占比最高,涉及代码多、难修复,还列举了 20 种错误类型,像软件崩溃、编译问题最常见,感知、定位、规划模块最容易出问题。
鲁棒性提升难题:为提升可靠性,常用冗余备份(如多硬件模块或多次运行软件对比输出),但会增加计算资源、功耗和芯片面积。比如特斯拉自动驾驶系统为保障鲁棒性,芯片面积大幅增加,负担明显。
自适应冗余设计:发现机器人系统不同节点鲁棒性不同,前端(感知、定位)鲁棒性高,后端(规划、控制)低。于是提出 “定制保护”:高鲁棒性节点用软件保护(如异常值检测,低延迟低功耗),低鲁棒性节点用硬件保护(如硬件冗余备份),既保可靠又减负担,还能按模块细化保护方式。
第 13 章:具身智能的数据挑战
数据价值大:具身智能数据是机器人能力训练的核心,像互联网数据用于广告推荐一样,具身智能数据用于优化机器人。预测其市场价值超互联网三倍,潜力巨大,但目前发展还在初期。
数据瓶颈突出:
质量与多样性难保障:训练具身智能需大量高质量、多样化数据。比如机器人要处理环境数据提升避障规划能力,工业机器人依赖精准数据,家用机器人得适应多样家庭环境数据,采集难度高、成本大。
数据孤岛严重:机器人组织采集数据局限于特定场景,缺乏共享,导致重复劳动和资源浪费,形成 “数据孤岛”,阻碍技术发展。
数据采集优化:设计 AIRSPEED 数据过滤系统,解决数据延迟、带宽不足、质量不稳问题。通过动态调整采集策略,结合数据压缩、关键帧选择等机制,应对不同传感器数据流量,确保在网络带宽有限时也能高效采集数据。
-
发表了主题帖:
《具身智能机器人系统》读后感,第五到九章
第 5 章:自主机器人的定位系统
定位作用:自主机器人要在环境里自主导航,得先知道自己的位置和朝向。就像人出门要知道自己在哪儿,机器人也得建立 “坐标系”—— 一个是自身的机器人坐标系,一个是环境的全局坐标系,通过定位系统算清自己在全局坐标系里的位置。
技术实现:现在机器人用多传感器融合定位,比如 GNSS、IMU 直接获取位置运动信息,摄像头、激光雷达测周围物体距离,再反推位置。但传感器数据有噪声,得用卡尔曼滤波这些方法优化,提升定位精度。
计算系统挑战:定位需要高精度、实时性,但传感器数据处理复杂,多传感器融合又考验算力和功耗。比如自动驾驶定位每秒输出多次位置,数据对齐(空间对齐、时间对齐)很关键,稍有误差(像摄像头和 IMU 时间差 40ms,定位误差能到 10 米)就会影响结果。硬件平台选型也难,得在算力、功耗、实时性之间平衡,现在 FPGA 因可重构、低功耗成主流。
第 6 章:自主机器人的规划与控制系统
规划核心:机器人在复杂环境里导航,得规划路径和轨迹。路径规划是找从起点到目标的路线,分 “可行路径”(只要能走)和 “最优路径”(优化时间、平滑度等);轨迹规划更复杂,要考虑时间参数、动态障碍物,甚至机器人运动学约束(比如最大曲率)。
强化学习应用:强化学习让机器人像人类一样从经验中学习,通过和环境互动优化决策。比如在行为决策中,应对复杂交通场景,强化学习能处理 “长尾” 情况(罕见但重要的场景);在规划控制中,设计合适的状态空间、动作空间,让机器人学会动态环境下的路径规划和实时控制,突破传统方法处理不确定性的局限。
第 7 章:具身智能机器人大模型
发展背景:ChatGPT 等大模型火了后,大家想把它用在机器人上。一开始尝试用自然语言让大模型控制机器人,发现有局限,后来就开发多模态大模型,融合视觉、听觉、文本等传感器数据,控制机器人。现在专门针对机器人任务的专用模型出现,精度比通用模型更高。
研究现状:2023 年后,大模型控制机器人成热门,像 ChatGPT for Robotics、Robotic Transformers 这些工作,探索大模型如何提升机器人能力。虽然大模型有潜力,但也有局限性,现在还在研究衡量模型的关键指标,以及未来发展方向,比如怎么更好结合大模型与机器人技术,推动行业变革。
ChatGPT for Robotics:以前机器人靠程序员手动编程,效率低、成本高、迭代慢。比如协作机器人调参数麻烦,任务一变就得重新编程。ChatGPT 出现后,靠强大语言理解和程序生成能力,能零样本学习规划任务,不用针对特定机器人预训练,给提示词就能互动,解决传统编程的迭代慢问题,但它也有局限性,比如实际控制中不够精准。
Robotic Transformers:谷歌提出的多模态大模型 RT,目标是建通用机器人学习系统。RT-1 通过图像和自然语言指令输入,双编码器设计融合信息,用大规模数据集(机器人课堂数据)训练,在复杂任务上成功率超 60%。后续 RT-2 扩大模型参数,编码机器人动作,突破动作函数库限制,让机器人能完成更多操作。
第 8 章:大模型用于机器人计算,颠覆还是进步
算法开发者视角:大模型让机器人在医疗、工业有新应用。医疗领域,多模态大模型辅助手术分析、缩短手术时间,未来手术室可能变成智能空间;工业领域,传统机器人靠编程,任务更新麻烦,具身智能让机器人能自主规划动作,适应新任务,提升工厂自动化效率。
系统开发者视角:机器人系统引入大模型有两种思路。一是 “端到端设计”,像谷歌 PaLM-E 和 RT 系列,让大模型处理多数任务,但实时性难保证,对算力、通信要求高;二是 “改进传统系统”,用大模型取代传统决策模块,保留定位、避障等基础模块,兼顾实时性和实用性,但数据反馈可能割裂大模型能力。现在第二种思路效果较好,但端到端设计潜力更大。
现状指标:具身智能机器人需关注成功率、实时性、安全性等指标。比如大模型参与后,实时性挑战明显,通信和算力跟不上就会影响机器人表现。
第 9 章:构建具身智能基础模型
元学习:让模型快速适应新任务,核心是 “学习如何学习”。通过多个任务训练提取共性知识,遇到新任务用共性快速调整,像机器人通过元学习在不同环境任务中优化行为,不用每次从头学。
模型预训练:选大规模多样化数据集(文本、图像等),用无监督或自监督学习,让模型掌握特征提取和知识储备。比如语言模型预训练预测下一个词,图像模型学分类检测,预训练后模型泛化能力强,后续微调更高效。
模型微调:在预训练基础上,用特定任务数据训练,让模型适应具体场景。比如预训练模型有通用能力,微调时针对机器人抓取任务优化,用小数据集让模型在特定任务表现更优,平衡通用与专用需求。
-
发表了主题帖:
《具身智能机器人系统》读后感,第三、四章
第 3 章:机器人计算系统
机器人基础:1920 年 “机器人” 一词诞生,现在机器人不只是像人的机器,扫地机器人、自动驾驶汽车都算。机器人计算系统超重要,靠软件算法和硬件配合完成任务,比如自动驾驶计算系统,得处理环境感知、决策规划这些。
自主机器人:分自主和非自主。自主机器人能自己感知环境、决策,像自动驾驶汽车;非自主就是固定任务,比如工厂机械臂。自主机器人的计算系统更复杂,得平衡算法精度、实时性。
硬件软件:硬件得满足大算力(跑复杂模型)、低功耗(机器人常用电池)、高实时性(快速反应)。软件架构有端到端和模块化,各有优缺点,现在还结合大模型提升机器人能力。
第 4 章:自主机器人的感知系统
物体检测:机器人用摄像头、激光雷达识别物体,像 HOG+SVM、Faster R-CNN 这些算法,就是让机器人看懂周围有啥东西,还能分类,确保在复杂环境里导航安全。
立体视觉与光流:立体视觉像人眼,用两个相机算深度;光流分析图像像素运动,帮机器人在动态环境里理解物体怎么动,提升感知精度。
鸟瞰视角感知(BEV):把传感器数据转成鸟瞰图,融合多传感器信息,方便后续规划决策。分激光雷达、相机、融合三类 BEV 方法,优势是直观、易融合数据,现在超受关注。
从目前实际看,端测部署大模型的可能性不大,11b\7b之类的模型跑在jetson上还是有可能,比如agx、nx系列,其他更大参数量的模型基本无望。
BEV这个在端到端的智驾方案中用的比较多,多传感器之间的时间同步是我碰到过的问题
- 2025-03-31
-
发表了主题帖:
又见PTX,烤机神器Jetson GPU Burn实测
视频讲解:又见PTX,烤机神器Jetson GPU Burn实测_哔哩哔哩_bilibili
jetson核心板测试gpu的应用程序需要考虑多程序占用gpu时的影响及gpu满负荷时温度的变化,影响着硬件、功耗、散热设计,如下为gpu的一个烤机程序
git clone https://github.com/anseeto/jetson-gpu-burn.git
cd jetson-gpu-burn
make -j6
./script.sh
烤机脚本如下,使用stress压测8核,使用gpu_burn 跑满 gpu
xterm -e stress --cpu 8 &
xterm -e ./gpu_burn 100000 &
tegrastats
compare.cu
CUDA是NVIDIA提供的高级GPU编程框架,通过扩展C/C++语法实现并行计算。它抽象了硬件细节(如线程调度、内存模型),允许开发者专注于算法逻辑
extern "C" __global__ void compare(float *C, int *faultyElems, size_t iters) {
size_t iterStep = blockDim.x*blockDim.y*gridDim.x*gridDim.y;
size_t myIndex = (blockIdx.y*blockDim.y + threadIdx.y)* // Y
gridDim.x*blockDim.x + // W
blockIdx.x*blockDim.x + threadIdx.x; // X
int myFaulty = 0;
for (size_t i = 1; i < iters; ++i)
if (fabsf(C[myIndex] - C[myIndex + i*iterStep]) > EPSILON)
myFaulty++;
atomicAdd(faultyElems, myFaulty);
}
compare.ptx
PTX(Parallel Thread Execution)是NVIDIA设计的中间指令集,介于CUDA代码与GPU机器码(SASS)之间,类似于虚拟汇编语言。它提供硬件无关性,支持跨代GPU兼容,但需要依赖驱动程序即时编译(JIT)为具体架构的二进制代码,相较于CUDA的抽象层,PTX允许开发者直接操作底层硬件资源。
- 2025-03-30
-
发表了主题帖:
MuJoCo 可视化键盘控制球体及位姿实时记录,附代码!
视频讲解:
今天分享的内容是,在Mujoco场景中添加一个球体,通过方向键控制球体x,y,z移动,至于这个场景可以用来做什么,先留个悬念,下面开始分享:
代码仓库:GitHub - LitchiCheng/mujoco-learning
创建球体场景的xml描述,如下:
<mujoco>
<visual>
<headlight diffuse="0.6 0.6 0.6" ambient="0.3 0.3 0.3" specular="0 0 0"/>
<rgba haze="0.15 0.25 0.35 1"/>
<global azimuth="140" elevation="-30"/>
</visual>
<asset>
<texture type="skybox" builtin="gradient" rgb1="0.3 0.5 0.7" rgb2="0 0 0" width="512" height="3072"/>
<texture type="2d" name="groundplane" builtin="checker" mark="edge" rgb1="0.2 0.3 0.4" rgb2="0.1 0.2 0.3"
markrgb="0.8 0.8 0.8" width="300" height="300"/>
<material name="groundplane" texture="groundplane" texuniform="true" texrepeat="5 5" reflectance="0.2"/>
</asset>
<worldbody>
<geom name="floor" size="0 0 0.05" type="plane" material="groundplane"/>
<body name="ball" pos="0 0 1">
<freejoint name="free_joint"/>
<geom type="sphere" size="0.2" rgba="1 0 0 1" mass="1"/>
</body>
</worldbody>
</mujoco>
键盘的按键获取使用pynput库,如下
from pynput import keyboard
key_states = {
keyboard.Key.up: False,
keyboard.Key.down: False,
keyboard.Key.left: False,
keyboard.Key.right: False,
keyboard.Key.alt_l: False,
keyboard.Key.alt_r: False,
}
def on_press(key):
if key in key_states:
key_states[key] = True
def on_release(key):
if key in key_states:
key_states[key] = False
listener = keyboard.Listener(on_press=on_press, on_release=on_release)
listener.start()
上下左右控制x, y方向,Alt L和Alt R控制z方向,完整代码如下:
import mujoco
import mujoco.viewer
import numpy as np
from pynput import keyboard
key_states = {
keyboard.Key.up: False,
keyboard.Key.down: False,
keyboard.Key.left: False,
keyboard.Key.right: False,
keyboard.Key.alt_l: False,
keyboard.Key.alt_r: False,
}
def on_press(key):
if key in key_states:
key_states[key] = True
def on_release(key):
if key in key_states:
key_states[key] = False
listener = keyboard.Listener(on_press=on_press, on_release=on_release)
listener.start()
XML = """
<mujoco>
<visual>
<headlight diffuse="0.6 0.6 0.6" ambient="0.3 0.3 0.3" specular="0 0 0"/>
<rgba haze="0.15 0.25 0.35 1"/>
<global azimuth="140" elevation="-30"/>
</visual>
<asset>
<texture type="skybox" builtin="gradient" rgb1="0.3 0.5 0.7" rgb2="0 0 0" width="512" height="3072"/>
<texture type="2d" name="groundplane" builtin="checker" mark="edge" rgb1="0.2 0.3 0.4" rgb2="0.1 0.2 0.3"
markrgb="0.8 0.8 0.8" width="300" height="300"/>
<material name="groundplane" texture="groundplane" texuniform="true" texrepeat="5 5" reflectance="0.2"/>
</asset>
<worldbody>
<geom name="floor" size="0 0 0.05" type="plane" material="groundplane"/>
<body name="ball" pos="0 0 1">
<freejoint name="free_joint"/>
<geom type="sphere" size="0.2" rgba="1 0 0 1" mass="1"/>
</body>
</worldbody>
</mujoco>
"""
model = mujoco.MjModel.from_xml_string(XML)
model.opt.gravity = (0, 0, 0)
data = mujoco.MjData(model)
class CustomViewer:
def __init__(self, model, data):
self.handle = mujoco.viewer.launch_passive(model, data)
self.pos = 0.0001
def is_running(self):
return self.handle.is_running()
def sync(self):
self.handle.sync()
@property
def cam(self):
return self.handle.cam
@property
def viewport(self):
return self.handle.viewport
def run_loop(self):
while self.is_running():
ball_body_name = "ball"
pos = data.body(ball_body_name).xpos
quat = data.body(ball_body_name).xquat
print(f"Position: {pos}, Orientation: {quat}")
if key_states[keyboard.Key.up]:
data.qpos[0] += self.pos
if key_states[keyboard.Key.down]:
data.qpos[0] -= self.pos
if key_states[keyboard.Key.left]:
data.qpos[1] += self.pos
if key_states[keyboard.Key.right]:
data.qpos[1] -= self.pos
if key_states[keyboard.Key.alt_l]:
data.qpos[2] += self.pos
if key_states[keyboard.Key.alt_r]:
data.qpos[2] -= self.pos
mujoco.mj_step(model, data)
self.sync()
viewer = CustomViewer(model, data)
viewer.cam.distance = 3
viewer.cam.azimuth = 0
viewer.cam.elevation = -30
viewer.run_loop()
为什么Alt R无法控制小球z方向运动?
答案是,Mujoco默认开启z轴的重力,小球会自然落到地面
关闭添加如下代码
model.opt.gravity = (0, 0, 0)
- 2025-03-29
-
回复了主题帖:
Pinocchio 安装教程|机器人学的必备库
wangerxian 发表于 2025-3-29 16:06
视频看不到,是不是没添加上?
视频文章里面
-
发表了主题帖:
【逆解机械臂】Pinocchio+MuJuCo 仿真 CLIK 闭环控制!附代码
视频讲解:
今天的主题是使用 Pinocchio 结合 MuJuCo 给定机械臂末端位姿,IK出关节角进行控制,pinocchio官方示例,给出了clik的闭环逆运动学的示例
https://github.com/stack-of-tasks/pinocchio/blob/master/examples/inverse-kinematics.py
我们结合Mujoco进行一次仿真测试,给一个变化的末端位姿,进行关节角度控制
其中涉及到雅可比矩阵等,大家可以参考这篇文章《Introduction to Inverse Kinematics with Jacobian Transpose, Pseudoinverse and Damped Least Squares methods》
https://mathweb.ucsd.edu/~sbuss/ResearchWeb/ikmethods/iksurvey.pdf
下面是代码实现,已上传至仓库:https://github.com/LitchiCheng/mujoco-learning
import mujoco
import numpy as np
import glfw
from scipy.optimize import minimize
import numpy as np
import pinocchio
from numpy.linalg import norm, solve
def inverse_kinematics(current_q, target_dir, target_pos):
urdf_filename = '/home/dar/MuJoCoBin/mujoco-learning/franka_panda_description/robots/panda_arm.urdf'
# 从 URDF 文件构建机器人模型
model = pinocchio.buildModelFromUrdf(urdf_filename)
# 为模型创建数据对象,用于存储计算过程中的中间结果
data = model.createData()
# 指定要控制的关节 ID
JOINT_ID = 7
# 定义期望的位姿,使用目标姿态的旋转矩阵和目标位置创建 SE3 对象
oMdes = pinocchio.SE3(target_dir, np.array(target_pos))
# 将当前关节角度赋值给变量 q,作为迭代的初始值
q = current_q
# 定义收敛阈值,当误差小于该值时认为算法收敛
eps = 1e-4
# 定义最大迭代次数,防止算法陷入无限循环
IT_MAX = 1000
# 定义积分步长,用于更新关节角度
DT = 1e-2
# 定义阻尼因子,用于避免矩阵奇异
damp = 1e-12
# 初始化迭代次数为 0
i = 0
while True:
# 进行正运动学计算,得到当前关节角度下机器人各关节的位置和姿态
pinocchio.forwardKinematics(model, data, q)
# 计算目标位姿到当前位姿之间的变换
iMd = data.oMi[JOINT_ID].actInv(oMdes)
# 通过李群对数映射将变换矩阵转换为 6 维误差向量(包含位置误差和方向误差),用于量化当前位姿与目标位姿的差异
err = pinocchio.log(iMd).vector
# 判断误差是否小于收敛阈值,如果是则认为算法收敛
if norm(err) < eps:
success = True
break
# 判断迭代次数是否超过最大迭代次数,如果是则认为算法未收敛
if i >= IT_MAX:
success = False
break
# 计算当前关节角度下的雅可比矩阵,关节速度与末端速度的映射关系
J = pinocchio.computeJointJacobian(model, data, q, JOINT_ID)
# 对雅可比矩阵进行变换,转换到李代数空间,以匹配误差向量的坐标系,同时取反以调整误差方向
J = -np.dot(pinocchio.Jlog6(iMd.inverse()), J)
# 使用阻尼最小二乘法求解关节速度
v = -J.T.dot(solve(J.dot(J.T) + damp * np.eye(6), err))
# 根据关节速度更新关节角度
q = pinocchio.integrate(model, q, v * DT)
# 每迭代 10 次打印一次当前的误差信息
if not i % 10:
print(f"{i}: error = {err.T}")
# 迭代次数加 1
i += 1
# 根据算法是否收敛输出相应的信息
if success:
print("Convergence achieved!")
else:
print(
"\n"
"Warning: the iterative algorithm has not reached convergence "
"to the desired precision"
)
# 打印最终的关节角度和误差向量
print(f"\nresult: {q.flatten().tolist()}")
print(f"\nfinal error: {err.T}")
# 返回最终的关节角度向量(以列表形式)
return q.flatten().tolist()
def scroll_callback(window, xoffset, yoffset):
global cam
# 调整相机的缩放比例
cam.distance *= 1 - 0.1 * yoffset
def limit_angle(angle):
while angle > np.pi:
angle -= 2 * np.pi
while angle < -np.pi:
angle += 2 * np.pi
return angle
def main():
global cam
# 加载模型
model = mujoco.MjModel.from_xml_path('/home/dar/MuJoCoBin/mujoco_menagerie/franka_emika_panda/scene.xml')
data = mujoco.MjData(model)
# 打印所有 body 的 ID 和名称
print("All bodies in the model:")
for i in range(model.nbody):
body_name = mujoco.mj_id2name(model, mujoco.mjtObj.mjOBJ_BODY, i)
print(f"ID: {i}, Name: {body_name}")
# 初始化 GLFW
if not glfw.init():
return
window = glfw.create_window(1200, 900, 'Panda Arm Control', None, None)
if not window:
glfw.terminate()
return
glfw.make_context_current(window)
# 设置鼠标滚轮回调函数
glfw.set_scroll_callback(window, scroll_callback)
# 初始化渲染器
cam = mujoco.MjvCamera()
opt = mujoco.MjvOption()
mujoco.mjv_defaultCamera(cam)
mujoco.mjv_defaultOption(opt)
pert = mujoco.MjvPerturb()
con = mujoco.MjrContext(model, mujoco.mjtFontScale.mjFONTSCALE_150.value)
scene = mujoco.MjvScene(model, maxgeom=10000)
# 找到末端执行器的 body id
end_effector_id = mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_BODY, 'link7')
print(f"End effector ID: {end_effector_id}")
if end_effector_id == -1:
print("Warning: Could not find the end effector with the given name.")
glfw.terminate()
return
# 初始关节角度
initial_q = data.qpos[:7].copy()
print(f"Initial joint positions: {initial_q}")
theta = np.pi
R_x = np.array([
[1, 0, 0],
[0, np.cos(theta), -np.sin(theta)],
[0, np.sin(theta), np.cos(theta)]
])
x = 0.3
new_q = initial_q
while not glfw.window_should_close(window):
# 获取当前末端执行器位置
mujoco.mj_forward(model, data)
end_effector_pos = data.body(end_effector_id).xpos
print(f"End effector position: {end_effector_pos}")
if x < 0.6:
x += 0.004
new_q = inverse_kinematics(initial_q, R_x, [x, 0.2, 0.7])
data.qpos[:7] = new_q
# 模拟一步
mujoco.mj_step(model, data)
# 更新渲染场景
viewport = mujoco.MjrRect(0, 0, 1200, 900)
mujoco.mjv_updateScene(model, data, opt, pert, cam, mujoco.mjtCatBit.mjCAT_ALL.value, scene)
mujoco.mjr_render(viewport, scene, con)
# 交换前后缓冲区
glfw.swap_buffers(window)
glfw.poll_events()
# 清理资源
glfw.terminate()
if __name__ == "__main__":
main()
- 2025-03-28
-
回复了主题帖:
机械臂如何稳稳上桌?Mujoco场景修改实操
秦天qintian0303 发表于 2025-3-28 11:02
愈加的熟练了,主要是这里面的模型如何定义啊
跟据实际场景
-
发表了主题帖:
Pinocchio 安装教程|机器人学的必备库
视频讲解:
https://gepettoweb.laas.fr/doc/stack-of-tasks/pinocchio/master/doxygen-html/index.html
安装pinocchio,不要安装pip3 install pinocchio !最新的库叫pin
pip3 install pin
git clone https://github.com/Gepetto/example-robot-data.git
mkdir models && mv example-robot-data/ models
mkdir pinocchio && touch test_pinocchio.py
test_pinocchio.py
from pathlib import Path
from sys import argv
import pinocchio
# Load the urdf model
model = pinocchio.buildModelFromUrdf("/home/dar/MuJoCoBin/mujoco-learning/franka_panda_description/robots/panda_arm.urdf")
print("model name: " + model.name)
# Create data required by the algorithms
data = model.createData()
# Sample a random configuration
q = pinocchio.randomConfiguration(model)
print(f"q: {q.T}")
# Perform the forward kinematics over the kinematic tree
pinocchio.forwardKinematics(model, data, q)
# Print out the placement of each joint of the kinematic tree
for name, oMi in zip(model.names, data.oMi):
print("{:<24} : {: .2f} {: .2f} {: .2f}".format(name, *oMi.translation.T.flat))
python3 test_pinocchio.py
-
回复了主题帖:
被Python版本逼疯?Miniconda环境隔离拯救你
hellokitty_bean 发表于 2025-3-28 08:51
conda帮你自动解决库依赖问题(尽管有时候也不一定能全部解决)
对喜欢亲力亲为的,原生好
解决依赖的过程本身就是熟悉代码的过程。
-
回复了主题帖:
被Python版本逼疯?Miniconda环境隔离拯救你
吾妻思萌 发表于 2025-3-28 08:16
我之前还看一个玩法 自己本地弄一个常用pandas numpy和其他的库,然后本地做库源,新的venv就从本地走,弄 ...
对的,有些公司有自己改过的库,会自己做发布源
-
回复了主题帖:
被Python版本逼疯?Miniconda环境隔离拯救你
吾妻思萌 发表于 2025-3-28 08:15
嗯嗯 我意思是conda我以前也有用但是有些公司网conda的服务器似乎有点问题
python 原生就还好,后面就 ...
基础环境我也是原生pthon,依赖大多数都能改好,不过对于很多只做算法的人就很费时间了
docker基本上就是都隔离了
- 2025-03-27
-
回复了主题帖:
被Python版本逼疯?Miniconda环境隔离拯救你
吾妻思萌 发表于 2025-3-27 16:02
不是 楼主 venv不是标配吗
别用啥conda miniconda 原生python 每次项目就放一个文件夹下,每次用自己的p ...
对,venv我视频里说了是python自带的
-
回复了主题帖:
被Python版本逼疯?Miniconda环境隔离拯救你
wangerxian 发表于 2025-3-27 15:54
用了这个之后,python环境有十几个
对,这是坏处
-
发表了主题帖:
机械臂如何稳稳上桌?Mujoco场景修改实操
视频讲解:
前面《常见机械臂模型不用找!Mujoco这儿都有!》中介绍的mujoco-menagerie中机械臂大多都是base_link放在地上的,这些场景往往和真实的场景对应不上,比如机械臂可能是安装在桌子上,或者安装在底盘上,所以就涉及到修改场景模型文件,今天我们就以so-arm100这个机械臂为例,为其添加一个桌子。
首先我们看下so-arm100原有的场景文件
<mujoco model="so_arm100 scene">
<include file="so_arm100.xml"/>
<statistic center="0 -0.2 0.1" extent="0.4"/>
<visual>
<headlight diffuse="0.6 0.6 0.6" ambient="0.3 0.3 0.3" specular="0 0 0"/>
<rgba haze="0.15 0.25 0.35 1"/>
<global azimuth="140" elevation="-30"/>
</visual>
<asset>
<texture type="skybox" builtin="gradient" rgb1="0.3 0.5 0.7" rgb2="0 0 0" width="512" height="3072"/>
<texture type="2d" name="groundplane" builtin="checker" mark="edge" rgb1="0.2 0.3 0.4" rgb2="0.1 0.2 0.3"
markrgb="0.8 0.8 0.8" width="300" height="300"/>
<material name="groundplane" texture="groundplane" texuniform="true" texrepeat="5 5" reflectance="0.2"/>
</asset>
<worldbody>
<light pos="0 0 1.5" dir="0 0 -1" directional="true"/>
<geom name="floor" size="0 0 0.05" type="plane" material="groundplane"/>
</worldbody>
</mujoco>
原有模型只描述了地面、天空等环境
我们可以参考aloha的场景,他是有桌子的,最起码桌子的mesh文件不用自己找了
然后在so-arm100的scene.xml上做修改,大概如下的步骤:
a. 首先需要把桌子的模型放进assets文件夹中
b. 然后添加路径说明
c. 添加mesh文件以及texture描述
d. 添加位置关系描述
修改后的文件如下,可以重新命名为scene_with_table.xml
<mujoco model="so_arm100 scene">
<compiler meshdir="assets" texturedir="assets"/>
<include file="so_arm100.xml"/>
<statistic center="0 -0.2 0.1" extent="0.4"/>
<visual>
<headlight diffuse="0.6 0.6 0.6" ambient="0.3 0.3 0.3" specular="0 0 0"/>
<rgba haze="0.15 0.25 0.35 1"/>
<global azimuth="140" elevation="-30"/>
</visual>
<asset>
<mesh file="tablelegs.obj"/>
<mesh file="tabletop.obj"/>
<texture type="skybox" builtin="gradient" rgb1="0.3 0.5 0.7" rgb2="0 0 0" width="512" height="3072"/>
<texture type="2d" name="groundplane" builtin="checker" mark="edge" rgb1="0.2 0.3 0.4" rgb2="0.1 0.2 0.3"
markrgb="0.8 0.8 0.8" width="300" height="300"/>
<texture type="2d" file="small_meta_table_diffuse.png"/>
<material name="groundplane" texture="groundplane" texuniform="true" texrepeat="5 5" reflectance="0.2"/>
<material name="table" texture="small_meta_table_diffuse"/>
</asset>
<worldbody>
<light pos="0 0 1.5" dir="0 0 -1" directional="true"/>
<geom name="floor" size="0 0 0.05" type="plane" material="groundplane" pos="0 0 -.75"/>
<site name="worldref" pos="0 0 -0.75"/>
<geom mesh="tabletop" material="table" class="visual" pos="0 0 -0.75" quat="1 0 0 1"/>
<geom mesh="tablelegs" material="table" class="visual" pos="0 0 -0.75" quat="1 0 0 1"/>
<geom name="table" pos="0 0 -0.1009" size="0.61 0.37 0.1" type="box" class="collision"/>
<camera name="overhead_cam" focal="1.93e-3 1.93e-3" resolution="1280 720" sensorsize="3896e-6 2140e-6"
pos="0 -0.303794 1.02524" mode="fixed" quat="0.976332 0.216277 0 0"/>
<camera name="worms_eye_cam" focal="1.93e-3 1.93e-3" resolution="1280 720" sensorsize="3896e-6 2140e-6"
pos="0 -0.377167 0.0316055" mode="fixed" quat="0.672659 0.739953 0 0"/>
</worldbody>
</mujoco>
simulate trs_so_arm100/scene_with_table.xml