
要在 IPython 启动时自动导入所需的模块最可靠的方法是借助其启动目录(startup)。IPython 会在这个目录里查找并执行指定的Python(.py)或IPython(.ipy)脚本文件从而在交互式会话开始前就自动完成所有预先配置的工作。环境准备找到startup目录操作的核心就是把你写好的自动导入脚本放到正确的startup文件夹里1. 找到配置文件目录打开终端(命令行界面输入ipython locate命令。这个命令会告诉你 IPython的配置文件目录在系统的哪个位置, 假设返回结果示例为/home/用户名/.ipython。实际路径可能因操作系统而异。$ ipython locate /home/blctrl/.ipython2. 进入startup目录根据上一步找到的路径进入startup文件夹。$ cd /home/blctrl/.ipython/profile_default/startup如果profile_default下没有 startup 文件夹请用下面命令新建$ mkdir -p /home/blctrl/.ipython/profile_default/startup3. 使用启动目录脚本1) 创建启动脚本在startup文件夹下创建一个Python文件。文件名可以随意但最好有意义。推荐使用数字前缀来控制脚本的执行顺序.(base) ~/.ipython/profile_default/startup$ ls 00_devices.py 01_writer.py 02_re.py README00_devices.py用于导入需要使用的设备:from ophyd import EpicsMotor tth EpicsMotor(PMC16C:m1, nametth) tth.wait_for_connection() th EpicsMotor(PMC16C:m2, nameth) th.wait_for_connection() chi EpicsMotor(PMC16C:m3, namechi) chi.wait_for_connection() phi EpicsMotor(PMC16C:m4, namephi) phi.wait_for_connection() from ophyd.scaler import EpicsScaler scaler EpicsScaler(NCT16:scaler1, namescaler) scaler.wait_for_connection() scaler.channels.read_attrs [chan1, chan2, chan3] scaler.channels.chan1.name PCT scaler.channels.chan2.name Mon scaler.channels.chan3.name Det scaler.preset_time.set(1.0)01_writer.py用于导入用于在本地指定路径下保存csv格式文件的python类:import csv import os from datetime import datetime class DataSaver: Bluesky 回调自动将扫描数据保存为 CSV 文件 def __init__(self, data_dir./data): self.data_dir data_dir os.makedirs(self.data_dir, exist_okTrue) self.file None self.writer None self.filename None def __call__(self, name, doc): if name start: # 生成文件名scan_{scan_id}_{时间戳}.csv scan_id doc.get(scan_id, 0) timestamp datetime.fromtimestamp(doc[time]).strftime(%Y%m%d_%H%M%S) self.filename os.path.join(self.data_dir, fscan_{scan_id}_{timestamp}.csv) self.file open(self.filename, w, newline) self.writer csv.writer(self.file) # 可写入元数据注释可选 self.writer.writerow([f# scan_id: {scan_id}]) self.writer.writerow([f# uid: {doc.get(uid, )}]) # 列名将在 descriptor 中确定先留空 self.headers_written False elif name descriptor: self.data_keys list(doc.get(data_keys, {}).keys()) if not getattr(self, headers_written, False): self.writer.writerow([seq_num, time] self.data_keys) self.headers_written True self.file.flush() elif name event: seq_num doc.get(seq_num, 0) t doc.get(time, 0) data doc.get(data, {}) row [seq_num, t] [data.get(k, None) for k in self.data_keys] self.writer.writerow(row) self.file.flush() elif name stop: if self.file: self.file.close() print(f数据已保存到: {self.filename})02_re.py用于导入需要使用的计划, 运行引擎的运行参数等:from bluesky.plans import scan, list_scan, grid_scan, rel_scan, count, rel_grid_scan, rel_grid_scan from bluesky.plan_stubs import mv, mvr, read from bluesky.callbacks.best_effort import BestEffortCallback from databroker import Broker from bluesky import RunEngine ds DataSaver(data_dir/home/bl0202/data) db Broker.named(temp) bec BestEffortCallback() RE RunEngine() RE.subscribe(db.insert) RE.subscribe(bec) RE.subscribe(ds)启动IPython:$ ipython Python 3.11.15 | packaged by conda-forge | (main, Mar 5 2026, 16:45:40) [GCC 14.3.0] Type copyright, credits or license for more information IPython 9.10.1 -- An enhanced Interactive Python. Type ? for help. Tip: You can use files !ls *.png In [1]: %matplotlib qt使用count计划计划进行测试:In [2]: RE(count([scaler], num5, delay1)) Transient Scan ID: 1 Time: 2026-05-14 16:18:56 Persistent Unique Scan ID: 4de04f99-40c3-4253-945d-ec8e7712da1f New stream: primary ----------------------------------------------------------- | seq_num | time | PCT | Mon | Det | ----------------------------------------------------------- | 1 | 16:18:57.2 | 1000000 | 0 | 0 | | 2 | 16:18:58.2 | 1000000 | 0 | 0 | | 3 | 16:18:59.3 | 1000000 | 0 | 0 | | 4 | 16:19:00.4 | 1000000 | 0 | 0 | | 5 | 16:19:01.4 | 1000000 | 0 | 0 | 数据已保存到: /home/bl0202/data/scan_1_20260514_161856.csv ----------------------------------------------------------- generator count [4de04f99] (scan num: 1)使用scan计划进行测试:prefont color#26A269n [/fontfont color#33DA7Ab4/b/fontfont color#26A269]: /fontRE(scan([scaler], tth, -font color#26A2691/font,font color#26A2691/font,font color#26A2696/font)) Transient Scan ID: 2 Time: 2026-05-14 16:19:34 Persistent Unique Scan ID: apos;52d0f735-e45d-4090-a1da-36dd16a18e52apos; New stream: apos;primaryapos; ----------------------------------------------------------------------- | seq_num | time | tth | PCT | Mon | Det | ----------------------------------------------------------------------- | 1 | 16:19:44.2 | -1.0000 | 1000000 | 0 | 0 | | 2 | 16:19:53.3 | -0.6000 | 1000000 | 0 | 0 | | 3 | 16:20:02.4 | -0.2000 | 1000000 | 0 | 0 | | 4 | 16:20:11.5 | 0.2000 | 1000000 | 0 | 0 | | 5 | 16:20:20.6 | 0.6000 | 1000000 | 0 | 0 | | 6 | 16:20:29.7 | 1.0000 | 1000000 | 0 | 0 | 数据已保存到: /home/bl0202/data/scan_2_20260514_161934.csv ----------------------------------------------------------------------- generator scan [apos;52d0f735apos;] (scan num: 2)使用list_scan计划进行测试:In [5]: RE(list_scan([scaler], tth, [-1,-0.5, 0, 0.5, 1])) Transient Scan ID: 3 Time: 2026-05-14 16:22:08 Persistent Unique Scan ID: 1935608f-37af-4fa3-ad8f-ca9846df61ac New stream: primary ----------------------------------------------------------------------- | seq_num | time | tth | PCT | Mon | Det | ----------------------------------------------------------------------- | 1 | 16:22:17.8 | -1.0000 | 1000000 | 0 | 0 | | 2 | 16:22:26.9 | -0.5000 | 1000000 | 0 | 0 | | 3 | 16:22:36.0 | 0.0000 | 1000000 | 0 | 0 | | 4 | 16:22:45.1 | 0.5000 | 1000000 | 0 | 0 | | 5 | 16:22:54.2 | 1.0000 | 1000000 | 0 | 0 | 数据已保存到: /home/bl0202/data/scan_3_20260514_162208.csv ----------------------------------------------------------------------- generator list_scan [1935608f] (scan num: 3)使用grid_scan进行测试:In [6]: RE(grid_scan([scaler], tth, -1,1,6, th,-2,2,6)) Transient Scan ID: 4 Time: 2026-05-14 16:23:26 Persistent Unique Scan ID: f1aabdbe-cd3b-4d75-81fe-9fa938195ccc New stream: primary ----------------------------------------------------------------------------------- | seq_num | time | tth | th | PCT | Mon | Det | ----------------------------------------------------------------------------------- | 1 | 16:23:45.7 | -1.0000 | -2.0000 | 1000000 | 0 | 0 | | 2 | 16:23:54.8 | -1.0000 | -1.2000 | 1000000 | 0 | 0 | ... | 35 | 16:29:46.8 | 1.0000 | 1.2000 | 1000000 | 0 | 0 | | 36 | 16:29:55.9 | 1.0000 | 2.0000 | 1000000 | 0 | 0 | 数据已保存到: /home/bl0202/data/scan_4_20260514_162326.csv ----------------------------------------------------------------------------------- generator grid_scan [f1aabdbe] (scan num: 4)同时测试数据也在指定路径下进行了保存::~/data$ ls scan_2_20260513_155455.csv scan_2_20260514_161934.csv scan_3_20260514_162208.csv scan_4_20260514_162326.csv使用 startup 目录是首选它更灵活可以组织多个脚本维护也方便。命名决定顺序文件会按字典序(即文件名顺序)执行。你可以用数字前缀来控制比如 01_imports.py 先于 02_settings.py 运行。.py vs .ipy如果脚本里只有 Python 代码(如 import ...)用 .py 即可。如果包含 IPython 特有的魔术命令如 %matplotlib则需要使用 .ipy 后缀。scripts目录扩展你也可以把自定义的Python脚本整个放在profile_default/startup/ 下当做一个可执行的库来自动导入实现隐式导入。