diff --git a/alignment_guide.md b/alignment_guide.md new file mode 100644 index 0000000..183848b --- /dev/null +++ b/alignment_guide.md @@ -0,0 +1,107 @@ +# 🛠️ FastUMI 数据采集:T265 坐标系对齐与复位指南 + +在使用基于 Intel RealSense T265 的手持设备(如 FastUMI)进行机器人操作数据采集时,**“对齐”(Alignment)或“复位”(Reset)是最关键的一步**。 + +本指南将通过图文为您详细说明为什么需要对齐,以及如何正确地进行物理操作。 + +--- + +## 1. 为什么要进行“对齐”? + +T265 追踪相机(视觉里程计)没有内建的绝对坐标感。当你启动录制程序的瞬间,T265 会武断地宣布: +**“我现在所在的这个点就是宇宙的中心 (0,0,0),我现在的朝向就是正前方!”** + +### ❌ 如果不对齐(错误示范) + +如果你随便找个地方按下了“开始录制”,那么每次生成的轨迹数据都会落在不同的坐标系中。机器人学习算法看到的数据就像下面这样混乱: + +```mermaid +graph TD + subgraph "现实物理空间 (目标杯子位置固定)" + Cup((杯子)) + end + + subgraph "T265 记录的数据 (因启动点不同而错乱)" + A["演示 1: 在远点按下启动"] -->|导致| X1(("数据记录: 杯子在 10m 处")) + B["演示 2: 在近点按下启动"] -->|导致| X2(("数据记录: 杯子在 2m 处")) + end + + style Cup fill:#f9f,stroke:#333,stroke-width:2px + style X1 fill:#ff9,stroke:#333,stroke-width:2px + style X2 fill:#ff9,stroke:#333,stroke-width:2px +``` + +### ✅ 正确的对齐(锚点效应) + +通过在每次点击“开始录制”之前,将手持设备放置在**同一个物理锚点**,我们就强行把 T265 的“随机坐标系”和“机器人的绝对坐标系”绑定在了一起。 + +```mermaid +graph TD + A["物理锚点\n(如:桌角)"] -->|固定位置启动| B("T265: 这里是 0,0,0") + B -->|config.json 转换| C{"机器人基座:\n 锚点固定位位姿"} + C -->|输出数据| D["所有动作轨迹对齐"] + + style A fill:#bbf,stroke:#333,stroke-width:2px + style D fill:#bfb,stroke:#333,stroke-width:2px +``` + +--- + +## 2. 三种实用的物理对齐方案 + +为了确保每次启动时的位置和角度完全一致,以下是实验室中最常用的三种方案(按精度从低到高排列): + +### 方案一:桌面标记法(十字胶带) +**精度:⭐ | 成本:极低** + +这是最简单的方案,适用于对绝对精度要求不极端的任务。 + +1. **准备**:在操作台面上,用有色胶带贴一个明显的“十”字作为对齐点。 +2. **标定**:测量这个“十”字中心到机器人真实底座的物理距离(X, Y, Z),将数值填入 `config.json` 的 `base_position`。 +3. **操作**: + - 每次录制前,将手持夹爪的尖端(TCP)或者设备底部的固定点**垂直对准**十字中心。 + - 保持设备的朝向(例如:始终垂直于桌子边缘)。 + - 点击“开始录制”。 + +--- + +### 方案二:物理挡块法(推荐,最常用) +**精度:⭐⭐⭐⭐ | 成本:低** + +这是性价比最高的方法,能有效限制位置和角度误差。 + +1. **准备**:在操作台边缘,使用重物(如两块方正的铅块)或 3D 打印一个直角底座(Dock),用强力双面胶固定在桌面上。 +2. **标定**:测量挡块的内角顶点到机器人基座的距离,填入 `base_position`。 +3. **操作**: + - 每次录制前,将手持设备的手柄底部**紧紧靠住**挡块的直角内部。 + - 确保设备的背面贴紧挡块的一侧(保证朝向一致)。 + - 确认设备稳定后,点击“开始录制”。 + - 录制开始后,再把设备拿起来去执行操作。 + +--- + +### 方案三:机器人夹持法(最高精度) +**精度:⭐⭐⭐⭐⭐ | 成本:高(需要操作机械臂)** + +如果你的终极目标是在真实机器人上进行毫米级的抓取,这是最严谨的方法。 + +1. **准备**:编写一个简单的脚本,让真实机械臂移动到一个固定的“Home”位置(例如正前方,姿态垂直向下)。 +2. **操作**: + - 在录制开始前,操作者拿着 FastUMI 采集器,将采集器的夹爪与真实机器人的夹爪(或法兰盘特定位置)**物理对接**(例如:互相咬合,或者靠死)。 + - 此时,采集器的位姿被机械臂死死限制住了。 + - 第二个人在电脑上点击“开始录制”。 + - 录制开始后,操作者移开采集器,去执行演示动作。 +3. **配置**:此时 `config.json` 中的 `base_position` 就是机器人这个“Home”位置的精确坐标。 + +--- + +## 3. 常见问题排查 (Troubleshooting) + +| 现象 | 可能原因 | 解决办法 | +| :--- | :--- | :--- | +| **机器人回放时,动作总是偏向一侧几厘米** | `base_position` 测量不准,或者物理锚点移动了。 | 重新拿卷尺精确测量锚点到机器人底座的距离并更新 config。 | +| **机器人回放时,高度(Z轴)越来越低,最后砸桌子** | T265 启动时的俯仰角(Pitch)没对准。启动时可能手抖让设备往下倾斜了。 | 使用**物理挡块法**,确保启动时设备是绝对垂直/水平的。 | +| **轨迹偶尔会“瞬移”或大范围漂移** | T265 在采集过程中镜头被遮挡,或者周围环境特征太少(如大面积白墙)。 | 保证相机视野清晰;在桌面上放一些有纹理的物体帮助 T265 定位。 | + +--- +**总结**:数据采集的真理是——**垃圾进,垃圾出 (Garbage In, Garbage Out)**。花 10 分钟搭建一个稳固的对齐挡块,能为你省去后期数天的除错时间。 diff --git "a/\345\270\270\347\224\250\344\273\243\347\240\201.txt" "b/\345\270\270\347\224\250\344\273\243\347\240\201.txt" new file mode 100644 index 0000000..4c692d2 --- /dev/null +++ "b/\345\270\270\347\224\250\344\273\243\347\240\201.txt" @@ -0,0 +1,244 @@ +去重 +df = df.drop_duplicates().reset_index(drop=True) +删除某一列 +df2=df2.drop(cols,axis=1) +删除两行 +df.drop(index=[0, 1]) + +date转字符串 + +from datetime import datetime, date, time +d = date.fromisoformat('2018-09-22') +t = time.fromisoformat('16:28:22') +dt = datetime.fromisoformat('2018-09-22') + +sdate = pd.to_datetime(ds).strftime('%Y-%m-%d') +st = "2019-01-20 00:00:00" +dt = datetime.datetime.strptime(st, '%Y-%m-%d %H:%M:%S') +st = "2019-01-20" +dt = datetime.datetime.strptime(st, '%Y-%m-%d') +start=pd.to_datetime('2017-01-01') + + + +修改类型 +train['tf_status'] = t1['tf_status'].astype(np.int64) + +修改日期类型 +df['ds'] = pd.to_datetime(df['ds']) + +修改字段名 +df.rename(columns={'#studentid':'studentid'}, inplace = True) + +#加年月 +df['year']=df.datetime.apply(lambda x: x.year) +df['month'] = df.datetime.apply(lambda x: x.month) +df['year'] = df['year'].astype(np.int64) +df['month'] = df['month'].astype(np.int64) + +删除字段 +df.drop(['#id'], axis=1, inplace=True) + +查看数据中顶部10%的数据 +print(df.weeks.quantile(np.arange(.9,1,.01))) + +设置索引 +df = df.set_index(['hetongdetailid']) + +判断空值 + +print("在 cat 列中总共有 %d 个空值." % df['cat'].isnull().sum()) + +print("在 review 列中总共有 %d 个空值." % df['review'].isnull().sum()) + +df[df.isnull().values==True] + +df = df[pd.notnull(df['review'])] + +排序 +df.sort_values(by="x1",ascending= False) + +模糊查询 +df_remark_tf[df_remark_tf["content_method"].str.contains(keystring)] + +修改字段类型 +df_appraise['deptid'] = df_appraise['deptid'].astype(np.int64) + +修改数据(https://blog.csdn.net/zhangchuang601/article/details/79583551) +df.loc[1,['name','age']] = ['bb',11] +df.iloc[1,2] = 19#修改某一无 +df.loc[df[df.htid.isin(ids)].index,"y"]=1 + +保存数据库 +df.to_sql(name='predict',con=mysql_engine,if_exists = 'replace') + +生成日期 +dt = datetime.datetime(year, month, 1) + +查看顶部10%数据分布 +print(movie_rating_count['totalRatingCount'].quantile(np.arange(.9,1,.01))) + +#-------plotly.express------------------------- +#折线图 +import plotly.express as px + +fig = px.line(df, x='date', y='y_true', + labels={'date':'日期', 'y_true':'话务量'}, + markers=True) +fig.update_xaxes(tickformat = "%Y-%m-%d", hoverformat = "%Y-%m-%d") +fig.update_layout(title_text="热线部门日业务量趋势图", title_x=0.5) +fig.update_traces(marker=dict(size=3)) #控制点的大小 +fig.show() + +#散点图 +fig = px.scatter(df, x="真实订单量", y="真实金额",hover_data=['did','日期']) +fig.update_traces(marker=dict(size=4)) #点的大小 +fig.show() + +---------------------------------- +1、nohup command & +例如 nohup jupyter notebook & + +2、找到进程PID(关闭在前面后台执行的进程的步骤,首先找到其进程PID) +ps -ef | grep xxxx +ps -ef 查看本机所有的进程;grep xxxx代表过滤找到条件xxxx的项目 + +3、kill掉进程 +kill -9 具体的PID + +-------------打开指定端口------------------------------------- +firewall-cmd --zone=public --add-port=8504/tcp --permanent +firewall-cmd --reload + +netstat -ntlp //查看当前所有tcp端口· +netstat -ntulp |grep 8888 //查看所有1935端口使用情况 + +------------------更新pip命令---------------------------------------- +python -m pip install --upgrade pip + +-------------搭建虚拟环境----------------------------- +# 创建虚拟环境 +conda create --name yourenvname python=3.8 + +# 进入虚拟环境 +conda activate yourenvname + +#退出虚拟环境 +conda deactivate + +#删除虚拟环境 +conda remove -n py39 --all + +# 在jupyter notebook中添加虚拟环境 +python -m ipykernel install --user --name yourenvname --display-name "display-name" + + +------------------在jupyter notebook里面添加虚拟环境---------------------------------------- +1.cmd进入虚拟环境(torch_env) +activate torch_env + +2.pip install ipykernel ipython +回车 + +3.ipython kernel install --user --name torch_env +回车 + +4.再次进入jupyter notebook +右上角,new,即可选择需要的虚拟环境。 + +5.另外,如果需要在指定文件夹中打开jupyter notebook,只需要打开文件夹所在位置,点击搜索框左边的位置框,输入cmd,再输入jupyter notebook,即可将路径设为自己需要的。 + +在jupyter 中删除虚拟环境 +6.jupyter kernelspec uninstall myenv + +---------pip install 镜像安装----------------------------------------- +pip install package_name -i https://pypi.tuna.tsinghua.edu.cn/simple + +--------------------图个格式转换--------------------------------------------------- +from PIL import Image + +#base64转PIL +def base64_to_pil(img_base64): + base64_decoded = base64.b64decode(img_base64) + byte_stream = io.BytesIO(base64_decoded) + pil_image = Image.open(byte_stream) + return pil_image + +#PIL转base64 +def image_to_base64_str(pil_image): + byte_arr = io.BytesIO() + pil_image.save(byte_arr, format='PNG') + byte_arr = byte_arr.getvalue() + return str(base64.b64encode(byte_arr).decode('utf-8')) + +#数组转base64(方法一) +pil_image = Image.fromarray(cv2.cvtColor(image_array, cv2.COLOR_BGR2RGB)) +image_base64 = image_to_base64_str(pil_image) + +#数组转base64(方法二) +retval, buffer = cv2.imencode('.png', img_mask_face_array) +image_mask_face_base64 = base64.b64encode(buffer).decode('utf-8') + +# 将PIL转换为NumPy数组 +array_image = np.array(pil_image) +array_image = np.asarray(pil_image) + +#array to pil +from PIL import Image +pil_image = Image.fromarray(image_array) + + +音量调节 +alsamixer + +常用开机自启管理命令总结 +拷贝.service文件: sudo cp mcp_pipe.service /etc/systemd/system/mcp_pipe.service +赋权限:sudo chmod 644 /etc/systemd/system/mcp_pipe.service +重新加载 systemd 配置:sudo systemctl daemon-reload + +启用开机自启: sudo systemctl enable mcp_pipe.service +禁止开机自启: sudo systemctl disable mcp_pipe.service +立即启动: sudo systemctl start mcp_pipe.service +立即停止: sudo systemctl stop mcp_pipe.service +重启服务: sudo systemctl restart mcp_pipe.service +查看状态: sudo systemctl status mcp_pipe.service +查看实时日志:journalctl -u mcp_pipe.service -f + +第一步:在 GitLab 上创建一个新的空项目 +在您的浏览器中打开 GitLab,并导航到 xiaozhi 组。 +点击页面右上角的蓝色按钮 "New project"。 +选择 "Create blank project"。 +在 "Project name" 字段中,为您的项目命名,例如 simple-py-xiaozhi。 +确保 "Project slug" 和 "Project URL" 是正确的。 +您可以选择将 "Visibility Level" 设置为 "Private" 或 "Internal"。 +不要 勾选 "Initialize repository with a README"。因为您本地已经有项目了,所以要创建一个空的项目。 +点击 "Create project"。 + +1. cd D:\work\py-xiaozhi\simple-py-xiaozhi +2. git init +3. git remote add origin [您在GitLab上创建的项目的URL] +4. git add . +5. git commit -m "Initial commit" +6. git push -u origin main + +---------esp32编译---------------------- +cd ~/work/esp-idf +source ./export.sh + +1. idf.py set-target esp32s3 +2. idf.py menuconfig +3. idf.py build +------生成单个固件文件----- +4. cd build , +5. python3 -m esptool --chip esp32s3 merge_bin -o ../xiaozhi_ok.bin @flash_args +6. 烧录固件: +python -m esptool --chip esp32s3 -p /dev/tty.usbmodemXXXX -b 460800 --before default_reset --after hard_reset write_flash -z 0x0 xiaozhi.bin + + + + + + + + +