init
This commit is contained in:
commit
c3e37f642a
94
.claude/skills/literature-search-verify/SKILL.md
Normal file
94
.claude/skills/literature-search-verify/SKILL.md
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
---
|
||||||
|
name: literature-search-verify
|
||||||
|
description: Search academic literature across arXiv, Semantic Scholar, Crossref, and other connected paper-search MCP tools, and independently verify every candidate citation before it is treated as real. Use this whenever the user asks to find papers, search literature on a topic, build a reading list, compile related-work references, check whether a citation or bibliography entry actually exists, or prepare references to import into Zotero or a .bib file — especially in academic writing contexts where a fabricated citation would be a real problem. Also covers guiding the user to the Zotero Connector browser extension for Chinese-language sources (CNKI/知网, Wanfang/万方, VIP/维普) that have no public API and cannot be reached by search tools.
|
||||||
|
---
|
||||||
|
|
||||||
|
# 文献检索 + 反幻觉引用核查
|
||||||
|
|
||||||
|
## 为什么需要这个技能
|
||||||
|
|
||||||
|
大语言模型在编造论文引用这件事上非常擅长——生成的标题、作者、期刊名读起来都很像真的,但可能根本不存在,或者张冠李戴(把A论文的结论安在B论文头上)。这在正式学术写作里是不可接受的:一篇论文只要有一条编造的引用被发现,审稿人对全篇的信任都会崩塌。
|
||||||
|
|
||||||
|
所以这个技能的核心不是"搜索",而是"搜索之后不轻信"——每一条打算真正拿去引用的文献,都必须经过独立交叉验证,验证不通过的必须明确标出来,而不是悄悄丢弃或者悄悄当作真的用。
|
||||||
|
|
||||||
|
## 工作流程
|
||||||
|
|
||||||
|
### 第一步:明确检索范围
|
||||||
|
|
||||||
|
在开始搜索前,搞清楚(不确定就直接问,一句话就够):
|
||||||
|
- 核心关键词/研究方向(可以中英文混合,比如"UAV磁补偿 Tolles-Lawson"这类)
|
||||||
|
- 大致的时间范围(比如"近5年"还是不限)
|
||||||
|
- 是否需要限定顶会/顶刊,还是什么来源都要
|
||||||
|
|
||||||
|
### 第二步:检索——直接调用脚本,不要自己现编API调用
|
||||||
|
|
||||||
|
`scripts/` 目录下已经写好了能直接跑的检索脚本,不依赖任何第三方Python包,也不需要装MCP工具:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 一次性搞定:检索 arXiv + Semantic Scholar + Crossref,自动去重、逐条验证,
|
||||||
|
# 并把通过验证的条目写成BibTeX文件——这是应该默认调用的入口
|
||||||
|
python3 scripts/literature_search.py "UAV magnetic compensation Tolles-Lawson" \
|
||||||
|
--max-per-source 8 --bib-out refs.bib
|
||||||
|
```
|
||||||
|
|
||||||
|
正常情况下**只需要跑这一条命令**,它内部会依次调用 `search_arxiv.py`、`search_semantic_scholar.py`、`search_crossref.py` 做检索,再对每条合并后的候选文献跑 `verify_citation.py` 做交叉验证,输出一份JSON报告(每条候选都带`verdict`字段)。如果只是想单独查一个来源,或者针对某一条文献单独复核,再分别调用对应的单个脚本(用法见每个脚本文件开头的docstring)。
|
||||||
|
|
||||||
|
如果这些脚本因为网络原因跑不动(比如内网/代理限制导致连不上 arxiv.org、semanticscholar.org、crossref.org),`literature_search.py` 会把每个来源的报错单独记在`search_errors`里而不是直接崩溃——这时候老实告诉用户"检索脚本连不上网络,以下是报错信息",不要退回去凭记忆编文献。如果用户这边确实连不上这几个学术API域名,才退回到 web_search 工具,并在结果里明确标注"来自通用网络搜索的补充结果,未经过脚本的交叉验证流程,置信度较低"。
|
||||||
|
|
||||||
|
如果用户已经连了 paper-search-mcp / scholar_mcp_server 这类MCP工具,可以补充用来扩大覆盖面(比如它们能覆盖PubMed、能直接下载PDF),但**不能替代**`verify_citation.py`的交叉验证这一步——MCP搜到的候选一样要过一遍验证,不能因为是工具搜出来的就默认可信。
|
||||||
|
|
||||||
|
### 第三步:理解验证结果——这是最关键的一步
|
||||||
|
|
||||||
|
`literature_search.py`(或单独调用`verify_citation.py`)对每条候选文献做的核查是:
|
||||||
|
|
||||||
|
1. **arXiv ID 独立核实**:如果有 arXiv ID,反查一次 arXiv API,确认这个ID真的存在且标题对得上——一个编造的ID在这一步会直接暴露。
|
||||||
|
2. **DOI 独立核实**:如果有 DOI,反查一次 Crossref,确认这个 DOI 真的能解析出对应文献。
|
||||||
|
3. **跨源标题复核**:不管有没有ID,单独拿标题去 Semantic Scholar 搜一次,要求返回的标题跟候选标题高度相似(相似度≥0.9)——这一步专门用来抓"标题作者读起来很像真的,但其实是编出来的"这种情况。
|
||||||
|
|
||||||
|
每条候选最后会带一个`verdict`:
|
||||||
|
- **verified**:至少一项独立核查通过,而且没有任何一项核查明确失败
|
||||||
|
- **suspect**:至少一项核查明确失败(比如DOI查不到、跨源标题对不上)——**这种情况下不要用这条文献,即使标题看起来很合适**
|
||||||
|
- **unverified**:所有核查项都因为网络等原因被跳过(`skipped`),不代表验证通过,只代表"没能验证"——**同样不能当成已核实的文献直接使用**,要跟用户说清楚原因
|
||||||
|
|
||||||
|
呈现给用户时按这三档分组说明,`suspect`和`unverified`都要明确标出来,不要因为报告里有个"看起来还行"的标题就含糊地当真的用。
|
||||||
|
|
||||||
|
**原则**:找不到真实存在的相关文献时,直接说"没找到符合条件的文献",不要为了凑数编一条出来。这条原则没有例外。
|
||||||
|
|
||||||
|
### 第四步:输出
|
||||||
|
|
||||||
|
按 verified / suspect / unverified 分组呈现结果,每条包含标题、作者年份、venue、标识符、一句话相关性说明。
|
||||||
|
|
||||||
|
`literature_search.py` 传了 `--bib-out` 参数时,会自动把所有 `verified` 的条目写成BibTeX文件,citation key 用"姓氏+年份"约定,可以直接导入 Zotero(配合 Better BibTeX 插件)。这些 key 也是后续`paper-writing-grounded`技能里`\cite{}`要用到的,两个技能之间通过这些key保持一致,不需要额外对照。
|
||||||
|
|
||||||
|
### 第五步:提醒中文文献的检索缺口
|
||||||
|
|
||||||
|
MCP 检索工具覆盖的是 arXiv/Semantic Scholar/Crossref 这类有公开 API 的英文为主的库,**知网、万方、维普这类中文数据库没有公开 API,搜不到很正常,不是技能出错**。遇到用户明显需要中文文献的场景,主动提醒:装好 Zotero Connector 浏览器插件,在浏览器里正常登录学校账号搜索、打开文献页面,点一下 Connector 图标就能把元数据和 PDF 存进 Zotero——这部分需要用户手动完成,不要尝试用检索工具"模拟"或"猜测"中文文献的存在。
|
||||||
|
|
||||||
|
### 第六步:归档
|
||||||
|
|
||||||
|
`output/` 目录只是脚本运行时的草稿区——里面混着每一轮探索性检索的原始JSON(包括被过滤掉的噪声,比如"Tolles""Lawson"被当成人名匹配出的无关文献),不适合作为最终交付物,而且随着会话增多会越堆越乱、也不方便下次会话或用户直接翻阅。
|
||||||
|
|
||||||
|
所以每次整理出一份**稳定可信的参考文献列表**(不管是第一轮检索还是后续多轮补充检索合并后的结果)之后,调用归档脚本把它固化到项目级目录,而不是留在技能自己的`output/`里:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 scripts/archive_references.py "UAV aeromagnetic compensation" \
|
||||||
|
--bib output/uav_aeromagnetic_compensation_final.bib \
|
||||||
|
--project-root . \
|
||||||
|
--pdfs-dir output/pdfs \
|
||||||
|
--suspect "某条可疑文献标题|不建议引用的具体原因" \
|
||||||
|
--notes "检索覆盖了哪些方向、哪些方向搜了但没结果、中文文献缺口提醒等"
|
||||||
|
```
|
||||||
|
|
||||||
|
这会在 `<project-root>/references/<按主题自动生成的slug>/` 下生成:
|
||||||
|
- `references.bib` —— 传入的bib文件原样拷贝过去
|
||||||
|
- `pdfs/`(如果传了`--pdfs-dir`且里面有PDF)—— 一并拷贝过去
|
||||||
|
- `README.md` —— 自动从bib里解析出条目列表(标题/年份/venue/DOI/note)生成索引,`--suspect`和`--notes`里的内容会分别整理进"不要引用"和"检索覆盖说明"两个小节
|
||||||
|
|
||||||
|
几个要点:
|
||||||
|
- `--bib` 传的必须是**已经过滤掉无关噪声、只保留verified条目**的干净bib文件,不要把`literature_search.py`直接吐出来的、可能夹杂噪声的原始bib不加甄别地拿去归档。
|
||||||
|
- 同一个`topic`名字多次调用会往同一个归档目录里覆盖更新(bib和README会被覆盖,pdfs按文件名去重合并),所以后续检索到更多文献后可以直接对同一个topic重新跑一遍归档脚本来更新,不需要手动合并。
|
||||||
|
- 这一步做完之后可以明确告诉用户归档目录的路径,方便他们后续在`paper-writing-grounded`阶段直接引用。
|
||||||
|
|
||||||
|
## 和 paper-writing-grounded 技能的配合
|
||||||
|
|
||||||
|
这个技能负责把"真实存在、经过核实的文献"整理好并生成 BibTeX;写作阶段的 paper-writing-grounded 技能会直接消费这里产出的 citation key,正文引用只能来自这里核实过的条目,不会凭空生成新的引用。两个技能配合使用时,建议先跑完这个技能、拿到稳定的参考文献列表,再进入写作。
|
||||||
@ -0,0 +1,165 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Archive a finished literature-search-verify session into a permanent,
|
||||||
|
project-level folder instead of leaving results sitting in the skill's
|
||||||
|
own scratch output/ directory (which is easy to lose track of across
|
||||||
|
sessions and isn't meant to be a durable deliverable location).
|
||||||
|
|
||||||
|
Bundles the verified BibTeX file -- and, if given, any downloaded PDFs --
|
||||||
|
into <project-root>/references/<topic-slug>/, and writes a README.md
|
||||||
|
index (entry list, suspect/unverified entries flagged separately, free-
|
||||||
|
text coverage notes) so a future session or a human can find and trust
|
||||||
|
what's there without re-reading the conversation that produced it.
|
||||||
|
|
||||||
|
No third-party dependencies; uses only the standard library.
|
||||||
|
|
||||||
|
CLI usage:
|
||||||
|
python3 archive_references.py "UAV aeromagnetic compensation" \\
|
||||||
|
--bib output/uav_aeromagnetic_compensation_final.bib \\
|
||||||
|
--project-root . \\
|
||||||
|
--pdfs-dir output/pdfs \\
|
||||||
|
--suspect "Some fabricated-looking title|DOI resolves but venue is topically unrelated" \\
|
||||||
|
--notes "Kalman-filter and GA/PSO angles searched, no on-topic hits found."
|
||||||
|
|
||||||
|
Output: prints the path of the archive directory that was created/updated.
|
||||||
|
"""
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import shutil
|
||||||
|
import sys
|
||||||
|
from datetime import date
|
||||||
|
|
||||||
|
|
||||||
|
def slugify(text):
|
||||||
|
text = text.strip().lower()
|
||||||
|
text = re.sub(r"[^a-z0-9]+", "_", text)
|
||||||
|
return text.strip("_")[:60] or "references"
|
||||||
|
|
||||||
|
|
||||||
|
def parse_bib_entries(bib_path):
|
||||||
|
"""Minimal BibTeX parser -- just enough to pull key/title/year/venue/doi/note
|
||||||
|
(plus the raw entry text, for reordering) for the README index. Not a
|
||||||
|
general-purpose BibTeX parser."""
|
||||||
|
with open(bib_path, encoding="utf-8") as f:
|
||||||
|
content = f.read()
|
||||||
|
|
||||||
|
entries = []
|
||||||
|
for m in re.finditer(r"@(\w+)\{([^,\n]+),(.*?)\n\}", content, re.S):
|
||||||
|
entry_type, key, body = m.groups()
|
||||||
|
fields = {}
|
||||||
|
for fm in re.finditer(r"(\w+)\s*=\s*\{(.*?)\}\s*,?\s*(?=\n\s*\w+\s*=|\n\Z|\Z)", body, re.S):
|
||||||
|
fields[fm.group(1).lower()] = re.sub(r"\s+", " ", fm.group(2)).strip()
|
||||||
|
entries.append({"type": entry_type, "key": key.strip(), "raw": m.group(0).strip(), **fields})
|
||||||
|
return entries
|
||||||
|
|
||||||
|
|
||||||
|
def year_sort_key(entry):
|
||||||
|
"""Chronological order, oldest first; entries with no parseable year sort last."""
|
||||||
|
year_str = re.sub(r"[^0-9]", "", entry.get("year", "") or "")
|
||||||
|
year = int(year_str) if year_str else 9999
|
||||||
|
return (year, entry.get("key", ""))
|
||||||
|
|
||||||
|
|
||||||
|
def build_readme(topic, entries, pdf_count, suspect, notes):
|
||||||
|
lines = []
|
||||||
|
lines.append(f"# {topic} — literature archive")
|
||||||
|
lines.append("")
|
||||||
|
lines.append(f"Archived: {date.today().isoformat()}")
|
||||||
|
lines.append(f"Verified entries: {len(entries)}")
|
||||||
|
lines.append(f"PDFs bundled: {pdf_count}")
|
||||||
|
lines.append("")
|
||||||
|
lines.append(
|
||||||
|
"Every entry in `references.bib` passed independent verification "
|
||||||
|
"(arXiv ID / DOI resolution and/or cross-source title match, "
|
||||||
|
"similarity >= 0.9) via the literature-search-verify skill before "
|
||||||
|
"being archived here. Citation keys follow the surname+year "
|
||||||
|
"convention and are stable -- the paper-writing-grounded skill's "
|
||||||
|
"`\\cite{}` calls should match these keys directly."
|
||||||
|
)
|
||||||
|
lines.append("")
|
||||||
|
lines.append("## Entries (chronological, oldest first)")
|
||||||
|
lines.append("")
|
||||||
|
for e in entries:
|
||||||
|
title = e.get("title", "?")
|
||||||
|
year = e.get("year", "?")
|
||||||
|
venue = e.get("journal") or e.get("booktitle") or e.get("school") or ""
|
||||||
|
doi = e.get("doi", "")
|
||||||
|
note = e.get("note", "")
|
||||||
|
line = f"- **{e['key']}** ({year}) — {title}"
|
||||||
|
if venue:
|
||||||
|
line += f". *{venue}*"
|
||||||
|
if doi:
|
||||||
|
line += f". DOI: {doi}"
|
||||||
|
lines.append(line)
|
||||||
|
if note:
|
||||||
|
lines.append(f" - Note: {note}")
|
||||||
|
|
||||||
|
if suspect:
|
||||||
|
lines.append("")
|
||||||
|
lines.append("## Flagged during search — NOT included above, do not cite")
|
||||||
|
lines.append("")
|
||||||
|
for s in suspect:
|
||||||
|
parts = s.split("|", 1)
|
||||||
|
title = parts[0].strip()
|
||||||
|
reason = parts[1].strip() if len(parts) > 1 else ""
|
||||||
|
lines.append(f"- {title}" + (f" — {reason}" if reason else ""))
|
||||||
|
|
||||||
|
if notes:
|
||||||
|
lines.append("")
|
||||||
|
lines.append("## Search coverage notes")
|
||||||
|
lines.append("")
|
||||||
|
lines.append(notes)
|
||||||
|
|
||||||
|
return "\n".join(lines) + "\n"
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||||
|
ap.add_argument("topic", help="Human-readable topic name, e.g. \"UAV aeromagnetic compensation\"")
|
||||||
|
ap.add_argument("--bib", required=True, help="path to the curated/verified .bib file to archive")
|
||||||
|
ap.add_argument("--project-root", default=".", help="project root; archive is written under <root>/references/<slug>/")
|
||||||
|
ap.add_argument("--pdfs-dir", default=None, help="optional folder of open-access PDFs to copy alongside the bib")
|
||||||
|
ap.add_argument("--suspect", action="append", default=[], help="title|reason of a suspect/unverified entry to log; repeatable")
|
||||||
|
ap.add_argument("--notes", default=None, help="free-text notes on search coverage/gaps for the README")
|
||||||
|
args = ap.parse_args()
|
||||||
|
|
||||||
|
if not os.path.isfile(args.bib):
|
||||||
|
print(f"error: bib file not found: {args.bib}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
slug = slugify(args.topic)
|
||||||
|
archive_dir = os.path.join(args.project_root, "references", slug)
|
||||||
|
os.makedirs(archive_dir, exist_ok=True)
|
||||||
|
|
||||||
|
bib_dest = os.path.join(archive_dir, "references.bib")
|
||||||
|
shutil.copyfile(args.bib, bib_dest)
|
||||||
|
entries = parse_bib_entries(bib_dest)
|
||||||
|
entries.sort(key=year_sort_key)
|
||||||
|
|
||||||
|
# Rewrite the archived .bib in chronological order (oldest first) so the
|
||||||
|
# file itself, not just the README, reads as a timeline.
|
||||||
|
header = f"% {args.topic} -- verified references, chronological order\n% Archived {date.today().isoformat()}\n\n"
|
||||||
|
with open(bib_dest, "w", encoding="utf-8") as f:
|
||||||
|
f.write(header)
|
||||||
|
f.write("\n\n".join(e["raw"] for e in entries))
|
||||||
|
f.write("\n")
|
||||||
|
|
||||||
|
pdf_count = 0
|
||||||
|
if args.pdfs_dir and os.path.isdir(args.pdfs_dir):
|
||||||
|
pdf_dest_dir = os.path.join(archive_dir, "pdfs")
|
||||||
|
os.makedirs(pdf_dest_dir, exist_ok=True)
|
||||||
|
for fn in sorted(os.listdir(args.pdfs_dir)):
|
||||||
|
if fn.lower().endswith(".pdf"):
|
||||||
|
shutil.copyfile(os.path.join(args.pdfs_dir, fn), os.path.join(pdf_dest_dir, fn))
|
||||||
|
pdf_count += 1
|
||||||
|
|
||||||
|
readme = build_readme(args.topic, entries, pdf_count, args.suspect, args.notes)
|
||||||
|
with open(os.path.join(archive_dir, "README.md"), "w", encoding="utf-8") as f:
|
||||||
|
f.write(readme)
|
||||||
|
|
||||||
|
print(archive_dir)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@ -0,0 +1,160 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
End-to-end literature search: query arXiv + Semantic Scholar + Crossref,
|
||||||
|
merge/dedupe candidates, independently verify each one, and emit both a
|
||||||
|
human-readable report and BibTeX for the entries that passed verification.
|
||||||
|
|
||||||
|
This is the one script Claude should actually call for a normal literature
|
||||||
|
search -- the individual search_*.py / verify_citation.py scripts exist
|
||||||
|
mainly as building blocks it can reuse for one-off / follow-up lookups.
|
||||||
|
|
||||||
|
CLI usage:
|
||||||
|
python3 literature_search.py "UAV magnetic compensation Tolles-Lawson" \\
|
||||||
|
--max-per-source 8 --bib-out refs.bib
|
||||||
|
|
||||||
|
Output: prints a JSON report to stdout (one entry per merged candidate,
|
||||||
|
with its verdict), and if --bib-out is given, writes BibTeX for every
|
||||||
|
"verified" entry to that file (never for "suspect" or "unverified" ones).
|
||||||
|
"""
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import argparse
|
||||||
|
import difflib
|
||||||
|
import re
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
|
||||||
|
from search_arxiv import search_arxiv
|
||||||
|
from search_semantic_scholar import search_s2
|
||||||
|
from search_crossref import search_crossref
|
||||||
|
from verify_citation import verify
|
||||||
|
|
||||||
|
|
||||||
|
def _similar(a, b, threshold=0.88):
|
||||||
|
if not a or not b:
|
||||||
|
return False
|
||||||
|
return difflib.SequenceMatcher(None, a.lower().strip(), b.lower().strip()).ratio() >= threshold
|
||||||
|
|
||||||
|
|
||||||
|
def merge_candidates(all_results):
|
||||||
|
"""Dedupe candidates that are the same paper found via multiple sources,
|
||||||
|
merging their metadata (preferring whichever source has an identifier)."""
|
||||||
|
merged = []
|
||||||
|
for item in all_results:
|
||||||
|
placed = False
|
||||||
|
for m in merged:
|
||||||
|
if _similar(item.get("title"), m.get("title")):
|
||||||
|
# merge: fill in any missing fields, keep track of all sources
|
||||||
|
for key in ("doi", "arxiv_id", "abstract", "venue", "year", "citation_count", "pdf_url"):
|
||||||
|
if not m.get(key) and item.get(key):
|
||||||
|
m[key] = item[key]
|
||||||
|
m["sources"] = sorted(set(m.get("sources", [m.get("source")]) + [item.get("source")]))
|
||||||
|
placed = True
|
||||||
|
break
|
||||||
|
if not placed:
|
||||||
|
item = dict(item)
|
||||||
|
item["sources"] = [item.get("source")]
|
||||||
|
merged.append(item)
|
||||||
|
return merged
|
||||||
|
|
||||||
|
|
||||||
|
def make_bibtex_key(candidate, used_keys):
|
||||||
|
authors = candidate.get("authors") or []
|
||||||
|
surname = "unknown"
|
||||||
|
if authors:
|
||||||
|
first_author = authors[0]
|
||||||
|
surname = first_author.strip().split()[-1].lower()
|
||||||
|
surname = re.sub(r"[^a-z]", "", surname) or "unknown"
|
||||||
|
year = str(candidate.get("year") or "nd")
|
||||||
|
base = f"{surname}{year}"
|
||||||
|
key = base
|
||||||
|
suffix = ord("a")
|
||||||
|
while key in used_keys:
|
||||||
|
key = f"{base}{chr(suffix)}"
|
||||||
|
suffix += 1
|
||||||
|
used_keys.add(key)
|
||||||
|
return key
|
||||||
|
|
||||||
|
|
||||||
|
def to_bibtex(candidate, key):
|
||||||
|
authors = candidate.get("authors") or []
|
||||||
|
author_str = " and ".join(authors) if authors else "Unknown"
|
||||||
|
title = candidate.get("title") or ""
|
||||||
|
year = candidate.get("year") or ""
|
||||||
|
venue = candidate.get("venue") or ""
|
||||||
|
doi = candidate.get("doi") or ""
|
||||||
|
arxiv_id = candidate.get("arxiv_id") or ""
|
||||||
|
|
||||||
|
if arxiv_id and not venue:
|
||||||
|
entry_type = "misc"
|
||||||
|
fields = [
|
||||||
|
("author", author_str),
|
||||||
|
("title", title),
|
||||||
|
("year", str(year)),
|
||||||
|
("eprint", arxiv_id),
|
||||||
|
("archivePrefix", "arXiv"),
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
entry_type = "article"
|
||||||
|
fields = [
|
||||||
|
("author", author_str),
|
||||||
|
("title", title),
|
||||||
|
("journal", venue),
|
||||||
|
("year", str(year)),
|
||||||
|
]
|
||||||
|
if doi:
|
||||||
|
fields.append(("doi", doi))
|
||||||
|
|
||||||
|
lines = [f"@{entry_type}{{{key},"]
|
||||||
|
for k, v in fields:
|
||||||
|
if v:
|
||||||
|
lines.append(f" {k} = {{{v}}},")
|
||||||
|
lines.append("}")
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
def run(query, max_per_source=8):
|
||||||
|
all_results = []
|
||||||
|
errors = {}
|
||||||
|
for name, fn in (("arxiv", search_arxiv), ("semantic_scholar", search_s2), ("crossref", search_crossref)):
|
||||||
|
try:
|
||||||
|
all_results.extend(fn(query, max_per_source))
|
||||||
|
except Exception as e:
|
||||||
|
errors[name] = str(e)
|
||||||
|
|
||||||
|
merged = merge_candidates(all_results)
|
||||||
|
|
||||||
|
used_keys = set()
|
||||||
|
for cand in merged:
|
||||||
|
result = verify(title=cand.get("title"), arxiv_id=cand.get("arxiv_id"), doi=cand.get("doi"))
|
||||||
|
cand["verdict"] = result["verdict"]
|
||||||
|
cand["verification_checks"] = result["checks"]
|
||||||
|
if result["verdict"] == "verified":
|
||||||
|
cand["bibtex_key"] = make_bibtex_key(cand, used_keys)
|
||||||
|
|
||||||
|
return {"query": query, "search_errors": errors, "candidates": merged}
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
ap = argparse.ArgumentParser(description=__doc__)
|
||||||
|
ap.add_argument("query")
|
||||||
|
ap.add_argument("--max-per-source", type=int, default=8)
|
||||||
|
ap.add_argument("--bib-out", default=None, help="path to write BibTeX for verified entries")
|
||||||
|
args = ap.parse_args()
|
||||||
|
|
||||||
|
try:
|
||||||
|
report = run(args.query, args.max_per_source)
|
||||||
|
except Exception as e:
|
||||||
|
print(json.dumps({"error": str(e)}, ensure_ascii=False))
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
print(json.dumps(report, ensure_ascii=False, indent=2))
|
||||||
|
|
||||||
|
if args.bib_out:
|
||||||
|
verified = [c for c in report["candidates"] if c["verdict"] == "verified"]
|
||||||
|
with open(args.bib_out, "w", encoding="utf-8") as f:
|
||||||
|
for cand in verified:
|
||||||
|
f.write(to_bibtex(cand, cand["bibtex_key"]))
|
||||||
|
f.write("\n\n")
|
||||||
|
sys.stderr.write(f"Wrote {len(verified)} verified BibTeX entries to {args.bib_out}\n")
|
||||||
@ -0,0 +1,80 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Search arXiv via its public Atom API. No API key required.
|
||||||
|
|
||||||
|
CLI usage:
|
||||||
|
python3 search_arxiv.py "UAV magnetic compensation" --max 10
|
||||||
|
|
||||||
|
Importable:
|
||||||
|
from search_arxiv import search_arxiv
|
||||||
|
"""
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
import argparse
|
||||||
|
import urllib.request
|
||||||
|
import urllib.parse
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
|
ARXIV_API = "http://export.arxiv.org/api/query"
|
||||||
|
NS = {"atom": "http://www.w3.org/2005/Atom"}
|
||||||
|
|
||||||
|
|
||||||
|
def search_arxiv(query, max_results=10, timeout=20):
|
||||||
|
params = {
|
||||||
|
"search_query": f"all:{query}",
|
||||||
|
"start": 0,
|
||||||
|
"max_results": max_results,
|
||||||
|
"sortBy": "relevance",
|
||||||
|
"sortOrder": "descending",
|
||||||
|
}
|
||||||
|
url = f"{ARXIV_API}?{urllib.parse.urlencode(params)}"
|
||||||
|
with urllib.request.urlopen(url, timeout=timeout) as resp:
|
||||||
|
data = resp.read()
|
||||||
|
root = ET.fromstring(data)
|
||||||
|
results = []
|
||||||
|
for entry in root.findall("atom:entry", NS):
|
||||||
|
id_el = entry.find("atom:id", NS)
|
||||||
|
title_el = entry.find("atom:title", NS)
|
||||||
|
summary_el = entry.find("atom:summary", NS)
|
||||||
|
published_el = entry.find("atom:published", NS)
|
||||||
|
if id_el is None or title_el is None:
|
||||||
|
continue
|
||||||
|
arxiv_id_full = id_el.text.strip()
|
||||||
|
arxiv_id = arxiv_id_full.rsplit("/", 1)[-1]
|
||||||
|
title = " ".join(title_el.text.split())
|
||||||
|
summary = " ".join(summary_el.text.split()) if summary_el is not None else ""
|
||||||
|
authors = [
|
||||||
|
a.find("atom:name", NS).text
|
||||||
|
for a in entry.findall("atom:author", NS)
|
||||||
|
if a.find("atom:name", NS) is not None
|
||||||
|
]
|
||||||
|
published = published_el.text[:10] if published_el is not None else None
|
||||||
|
pdf_url = None
|
||||||
|
for link in entry.findall("atom:link", NS):
|
||||||
|
if link.attrib.get("title") == "pdf":
|
||||||
|
pdf_url = link.attrib.get("href")
|
||||||
|
results.append({
|
||||||
|
"source": "arxiv",
|
||||||
|
"arxiv_id": arxiv_id,
|
||||||
|
"title": title,
|
||||||
|
"authors": authors,
|
||||||
|
"year": published[:4] if published else None,
|
||||||
|
"published": published,
|
||||||
|
"abstract": summary,
|
||||||
|
"pdf_url": pdf_url,
|
||||||
|
"doi": None,
|
||||||
|
})
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
ap = argparse.ArgumentParser(description=__doc__)
|
||||||
|
ap.add_argument("query")
|
||||||
|
ap.add_argument("--max", type=int, default=10)
|
||||||
|
args = ap.parse_args()
|
||||||
|
try:
|
||||||
|
out = search_arxiv(args.query, args.max)
|
||||||
|
print(json.dumps(out, ensure_ascii=False, indent=2))
|
||||||
|
except Exception as e:
|
||||||
|
print(json.dumps({"error": str(e)}, ensure_ascii=False))
|
||||||
|
sys.exit(1)
|
||||||
@ -0,0 +1,65 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Search the Crossref works API. No API key required.
|
||||||
|
Good for journal articles / DOIs that arXiv and Semantic Scholar might miss.
|
||||||
|
|
||||||
|
CLI usage:
|
||||||
|
python3 search_crossref.py "UAV magnetic compensation" --max 10
|
||||||
|
|
||||||
|
Importable:
|
||||||
|
from search_crossref import search_crossref
|
||||||
|
"""
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
import argparse
|
||||||
|
import urllib.request
|
||||||
|
import urllib.parse
|
||||||
|
|
||||||
|
CROSSREF_API = "https://api.crossref.org/works"
|
||||||
|
UA = "literature-search-verify-skill/1.0 (mailto:research-assistant@example.com)"
|
||||||
|
|
||||||
|
|
||||||
|
def search_crossref(query, max_results=10, timeout=20):
|
||||||
|
params = {"query": query, "rows": max_results}
|
||||||
|
url = f"{CROSSREF_API}?{urllib.parse.urlencode(params)}"
|
||||||
|
req = urllib.request.Request(url, headers={"User-Agent": UA})
|
||||||
|
with urllib.request.urlopen(req, timeout=timeout) as resp:
|
||||||
|
data = json.loads(resp.read())
|
||||||
|
results = []
|
||||||
|
for item in data.get("message", {}).get("items", []) or []:
|
||||||
|
titles = item.get("title") or []
|
||||||
|
title = titles[0] if titles else ""
|
||||||
|
authors = []
|
||||||
|
for a in item.get("author", []) or []:
|
||||||
|
name = " ".join(filter(None, [a.get("given"), a.get("family")]))
|
||||||
|
if name:
|
||||||
|
authors.append(name)
|
||||||
|
year = None
|
||||||
|
date_parts = (item.get("issued", {}) or {}).get("date-parts")
|
||||||
|
if date_parts and date_parts[0]:
|
||||||
|
year = date_parts[0][0]
|
||||||
|
containers = item.get("container-title") or []
|
||||||
|
results.append({
|
||||||
|
"source": "crossref",
|
||||||
|
"title": title,
|
||||||
|
"authors": authors,
|
||||||
|
"year": year,
|
||||||
|
"venue": containers[0] if containers else None,
|
||||||
|
"doi": item.get("DOI"),
|
||||||
|
"arxiv_id": None,
|
||||||
|
"abstract": None,
|
||||||
|
})
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
ap = argparse.ArgumentParser(description=__doc__)
|
||||||
|
ap.add_argument("query")
|
||||||
|
ap.add_argument("--max", type=int, default=10)
|
||||||
|
args = ap.parse_args()
|
||||||
|
try:
|
||||||
|
out = search_crossref(args.query, args.max)
|
||||||
|
print(json.dumps(out, ensure_ascii=False, indent=2))
|
||||||
|
except Exception as e:
|
||||||
|
print(json.dumps({"error": str(e)}, ensure_ascii=False))
|
||||||
|
sys.exit(1)
|
||||||
@ -0,0 +1,59 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Search the Semantic Scholar Graph API. No API key required for light use;
|
||||||
|
set the S2_API_KEY environment variable for higher rate limits.
|
||||||
|
|
||||||
|
CLI usage:
|
||||||
|
python3 search_semantic_scholar.py "UAV magnetic compensation" --max 10
|
||||||
|
|
||||||
|
Importable:
|
||||||
|
from search_semantic_scholar import search_s2
|
||||||
|
"""
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import argparse
|
||||||
|
import urllib.request
|
||||||
|
import urllib.parse
|
||||||
|
|
||||||
|
S2_API = "https://api.semanticscholar.org/graph/v1/paper/search"
|
||||||
|
FIELDS = "title,authors,year,venue,externalIds,abstract,citationCount"
|
||||||
|
|
||||||
|
|
||||||
|
def search_s2(query, max_results=10, timeout=20):
|
||||||
|
params = {"query": query, "limit": max_results, "fields": FIELDS}
|
||||||
|
url = f"{S2_API}?{urllib.parse.urlencode(params)}"
|
||||||
|
req = urllib.request.Request(url)
|
||||||
|
api_key = os.environ.get("S2_API_KEY")
|
||||||
|
if api_key:
|
||||||
|
req.add_header("x-api-key", api_key)
|
||||||
|
with urllib.request.urlopen(req, timeout=timeout) as resp:
|
||||||
|
data = json.loads(resp.read())
|
||||||
|
results = []
|
||||||
|
for p in data.get("data", []) or []:
|
||||||
|
ext = p.get("externalIds") or {}
|
||||||
|
results.append({
|
||||||
|
"source": "semantic_scholar",
|
||||||
|
"title": p.get("title"),
|
||||||
|
"authors": [a.get("name") for a in (p.get("authors") or [])],
|
||||||
|
"year": p.get("year"),
|
||||||
|
"venue": p.get("venue"),
|
||||||
|
"doi": ext.get("DOI"),
|
||||||
|
"arxiv_id": ext.get("ArXiv"),
|
||||||
|
"abstract": p.get("abstract"),
|
||||||
|
"citation_count": p.get("citationCount"),
|
||||||
|
})
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
ap = argparse.ArgumentParser(description=__doc__)
|
||||||
|
ap.add_argument("query")
|
||||||
|
ap.add_argument("--max", type=int, default=10)
|
||||||
|
args = ap.parse_args()
|
||||||
|
try:
|
||||||
|
out = search_s2(args.query, args.max)
|
||||||
|
print(json.dumps(out, ensure_ascii=False, indent=2))
|
||||||
|
except Exception as e:
|
||||||
|
print(json.dumps({"error": str(e)}, ensure_ascii=False))
|
||||||
|
sys.exit(1)
|
||||||
@ -0,0 +1,122 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Independently cross-verify a single candidate citation. This is the anti-
|
||||||
|
hallucination check: it never trusts a single source. If a check cannot be
|
||||||
|
run at all (e.g. no network), that check is reported as "skipped" -- never
|
||||||
|
silently counted as a pass.
|
||||||
|
|
||||||
|
CLI usage:
|
||||||
|
python3 verify_citation.py --title "Compensation of magnetic ..." \\
|
||||||
|
--arxiv-id 2401.12345 --doi 10.1109/TGRS.2024.1234567
|
||||||
|
|
||||||
|
Importable:
|
||||||
|
from verify_citation import verify
|
||||||
|
"""
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
import argparse
|
||||||
|
import difflib
|
||||||
|
import urllib.request
|
||||||
|
import urllib.parse
|
||||||
|
import urllib.error
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
|
ATOM_NS = {"atom": "http://www.w3.org/2005/Atom"}
|
||||||
|
|
||||||
|
|
||||||
|
def title_similarity(a, b):
|
||||||
|
if not a or not b:
|
||||||
|
return 0.0
|
||||||
|
return difflib.SequenceMatcher(None, a.lower().strip(), b.lower().strip()).ratio()
|
||||||
|
|
||||||
|
|
||||||
|
def check_arxiv_id(arxiv_id, timeout=20):
|
||||||
|
"""Confirm an arXiv ID actually resolves to a real paper."""
|
||||||
|
try:
|
||||||
|
url = f"http://export.arxiv.org/api/query?id_list={urllib.parse.quote(arxiv_id)}"
|
||||||
|
with urllib.request.urlopen(url, timeout=timeout) as resp:
|
||||||
|
data = resp.read()
|
||||||
|
root = ET.fromstring(data)
|
||||||
|
entry = root.find("atom:entry", ATOM_NS)
|
||||||
|
if entry is None:
|
||||||
|
return {"status": "fail", "reason": "arXiv ID not found"}
|
||||||
|
title_el = entry.find("atom:title", ATOM_NS)
|
||||||
|
title = " ".join(title_el.text.split()) if title_el is not None else None
|
||||||
|
return {"status": "pass", "canonical_title": title}
|
||||||
|
except Exception as e:
|
||||||
|
return {"status": "skipped", "reason": str(e)}
|
||||||
|
|
||||||
|
|
||||||
|
def check_doi(doi, timeout=20):
|
||||||
|
"""Confirm a DOI actually resolves via Crossref."""
|
||||||
|
try:
|
||||||
|
url = f"https://api.crossref.org/works/{urllib.parse.quote(doi)}"
|
||||||
|
req = urllib.request.Request(
|
||||||
|
url, headers={"User-Agent": "literature-search-verify-skill/1.0"}
|
||||||
|
)
|
||||||
|
with urllib.request.urlopen(req, timeout=timeout) as resp:
|
||||||
|
data = json.loads(resp.read())
|
||||||
|
titles = data.get("message", {}).get("title") or []
|
||||||
|
return {"status": "pass", "canonical_title": titles[0] if titles else None}
|
||||||
|
except urllib.error.HTTPError as e:
|
||||||
|
if e.code == 404:
|
||||||
|
return {"status": "fail", "reason": "DOI not found in Crossref"}
|
||||||
|
return {"status": "skipped", "reason": f"HTTP {e.code}"}
|
||||||
|
except Exception as e:
|
||||||
|
return {"status": "skipped", "reason": str(e)}
|
||||||
|
|
||||||
|
|
||||||
|
def check_title_cross_source(title, timeout=20):
|
||||||
|
"""Independently re-search by title on a different source (Semantic
|
||||||
|
Scholar) and require a near-exact title match. This is what catches a
|
||||||
|
plausible-sounding but entirely invented title/author combination."""
|
||||||
|
try:
|
||||||
|
params = {"query": title, "limit": 3, "fields": "title"}
|
||||||
|
url = f"https://api.semanticscholar.org/graph/v1/paper/search?{urllib.parse.urlencode(params)}"
|
||||||
|
with urllib.request.urlopen(url, timeout=timeout) as resp:
|
||||||
|
data = json.loads(resp.read())
|
||||||
|
candidates = data.get("data", []) or []
|
||||||
|
if not candidates:
|
||||||
|
return {"status": "fail", "reason": "no matching title found on Semantic Scholar"}
|
||||||
|
best = max(candidates, key=lambda p: title_similarity(title, p.get("title", "")))
|
||||||
|
sim = title_similarity(title, best.get("title", ""))
|
||||||
|
if sim >= 0.9:
|
||||||
|
return {"status": "pass", "similarity": round(sim, 3), "matched_title": best.get("title")}
|
||||||
|
return {"status": "fail", "similarity": round(sim, 3), "matched_title": best.get("title")}
|
||||||
|
except Exception as e:
|
||||||
|
return {"status": "skipped", "reason": str(e)}
|
||||||
|
|
||||||
|
|
||||||
|
def verify(title=None, arxiv_id=None, doi=None):
|
||||||
|
checks = {}
|
||||||
|
if arxiv_id:
|
||||||
|
checks["arxiv_id_check"] = check_arxiv_id(arxiv_id)
|
||||||
|
if doi:
|
||||||
|
checks["doi_check"] = check_doi(doi)
|
||||||
|
if title:
|
||||||
|
checks["title_cross_source_check"] = check_title_cross_source(title)
|
||||||
|
|
||||||
|
passed = [c for c in checks.values() if c["status"] == "pass"]
|
||||||
|
failed = [c for c in checks.values() if c["status"] == "fail"]
|
||||||
|
|
||||||
|
if failed:
|
||||||
|
verdict = "suspect" # something actively contradicted it
|
||||||
|
elif passed:
|
||||||
|
verdict = "verified" # at least one independent check passed
|
||||||
|
else:
|
||||||
|
verdict = "unverified" # everything skipped (e.g. no network) -- NOT the same as verified
|
||||||
|
|
||||||
|
return {"title": title, "arxiv_id": arxiv_id, "doi": doi, "verdict": verdict, "checks": checks}
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
ap = argparse.ArgumentParser(description=__doc__)
|
||||||
|
ap.add_argument("--title", default=None)
|
||||||
|
ap.add_argument("--arxiv-id", default=None)
|
||||||
|
ap.add_argument("--doi", default=None)
|
||||||
|
args = ap.parse_args()
|
||||||
|
if not any([args.title, args.arxiv_id, args.doi]):
|
||||||
|
print(json.dumps({"error": "provide at least one of --title/--arxiv-id/--doi"}))
|
||||||
|
sys.exit(1)
|
||||||
|
result = verify(args.title, args.arxiv_id, args.doi)
|
||||||
|
print(json.dumps(result, ensure_ascii=False, indent=2))
|
||||||
50
.claude/skills/paper-wiriting-grounded/SKILL.md
Normal file
50
.claude/skills/paper-wiriting-grounded/SKILL.md
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
---
|
||||||
|
name: paper-writing-grounded
|
||||||
|
description: Draft, revise, or polish academic paper and thesis sections (abstract, introduction, methods, experiments, related work, conclusion) for LaTeX conference/journal templates or Word/WPS-ready Chinese theses, while strictly preventing fabricated numbers, invented experimental results, or unsupported quantitative claims. Use this whenever the user asks to write, draft, outline, or polish any section of a paper or dissertation, wants raw results turned into prose, needs a LaTeX draft matching a conference template, wants Word/WPS text for a Chinese-language thesis, or wants AI-sounding writing "humanized" — anywhere the draft could end up stating a number, percentage, or comparison that didn't actually come from real data.
|
||||||
|
---
|
||||||
|
|
||||||
|
# 论文写作(强制数据溯源版)
|
||||||
|
|
||||||
|
## 为什么需要这个技能
|
||||||
|
|
||||||
|
LLM写论文时最大的隐患不是文笔差,而是**在没有真实数据支撑的地方,顺手编一个"看起来合理"的数字**——比如"补偿后RMSE降低了23%"这种话,读起来完全正常,但如果这个23%不是从真实实验里来的,那就是数据捏造,一旦被发现是学术诚信问题,不是文笔问题。这个技能的核心规则就一条:**任何关于"我们的方法/我们的结果"的具体数字、百分比、对比、最高级描述,必须能追溯到用户提供的真实结果,追溯不到就必须明确标记出来,绝不能编一个数填上去。**
|
||||||
|
|
||||||
|
## 工作流程
|
||||||
|
|
||||||
|
### 第一步:建立"真实结果登记表"
|
||||||
|
|
||||||
|
在动笔写任何会出现具体数字的段落之前(尤其是实验/结果部分),先确认这一节要用到的真实数据来源——可以是用户粘贴的数字、一份CSV/JSON格式的指标文件、SimPEG/Harmonica处理流程跑出来的输出、图表标题里的数值等等。如果对话里还没有提供,直接问用户要,而不是先写着占位数字"等下再改"——占位数字很容易被忘记改掉,最后混进定稿。
|
||||||
|
|
||||||
|
把这一节会用到的每个具体数字记下来,连同它的出处(来自哪个文件/哪次实验/哪张图),这就是这一节写作时唯一可信的"真实结果登记表"。
|
||||||
|
|
||||||
|
### 第二步:分节起草
|
||||||
|
|
||||||
|
根据目标产出选结构:
|
||||||
|
- **英文期刊/会议投稿(LaTeX)**:Abstract → Introduction → Related Work → Methods → Experiments → Conclusion,先问清楚目标模板(NeurIPS/ICLR/ICML,或者你们学校的LaTeX模板),没问清楚就先按通用IMRAD结构起草,后面再套模板。
|
||||||
|
- **中文学位论文(Word/WPS)**:按论文各章节结构(通常是绪论/文献综述/方法/实验与结果/结论)起草,产出用docx技能生成格式化的Word文档。
|
||||||
|
|
||||||
|
### 第三步:强制溯源规则(核心)
|
||||||
|
|
||||||
|
起草时,每写到一个具体数字、百分比、显著性描述("显著优于"、"最优"这类)时,问自己:这个数字能不能在第一步的"真实结果登记表"里找到出处,或者能不能在literature-search-verify技能核实过的文献里找到支持?
|
||||||
|
|
||||||
|
- 能追溯到 → 正常写,行文里可以顺带标注来源(比如"如表2所示")
|
||||||
|
- 追溯不到 → **不要编数字填上去**,改成明确的占位标记:`[需要数据:补偿后与补偿前的RMSE对比数值]`,让用户知道这里缺什么、需要补什么,而不是假装写完了
|
||||||
|
|
||||||
|
这条规则同样适用于"我们首次提出"、"计算效率更高"这类没有具体数字但仍是实质性主张的表述——同样需要能落到某个真实依据上,落不到就标记出来,不要含糊带过。
|
||||||
|
|
||||||
|
### 第四步:自查
|
||||||
|
|
||||||
|
起草完一节后,回头把这一节里所有的数字、比较、最高级表述再过一遍,确认每一条都能对应到登记表或已核实的引用。在回复末尾列出这次自查发现的、还没解决的`[需要数据]`标记,方便用户一次性补齐,而不是散落在长文里被忽略。
|
||||||
|
|
||||||
|
### 第五步:去AI味润色
|
||||||
|
|
||||||
|
润色不等于压缩。除非用户明确要求精简,不要为了让句子读起来"更自然"而删掉具体的研究对象、数据口径、方法条件、指标定义这些内容——这些恰恰是审稿人会重点核对的地方。去掉的应该是空洞的模板化表达(比如"综上所述,本研究具有重要意义"这类没有信息量的套话)和明显的AI腔调用词,而不是数据本身的精度和限定条件。
|
||||||
|
|
||||||
|
### 第六步:产出格式
|
||||||
|
|
||||||
|
- LaTeX:正文里的`\cite{}`只使用literature-search-verify技能核实过、生成过BibTeX的citation key,不自己编新的引用键。
|
||||||
|
- Word/WPS:用docx技能产出格式化文档;提醒用户Zotero的Word插件可以配合插入引用,但WPS对该插件兼容性一般,可能需要先用Zotero导出RTF/纯文本引用再手动整理进WPS。
|
||||||
|
|
||||||
|
## 和 literature-search-verify 技能的配合
|
||||||
|
|
||||||
|
正文里任何"related work"或背景介绍部分引用的文献,只能来自literature-search-verify技能已核实的条目;这个技能不负责验证引用真实性,只负责确保"我们自己的实验结果"这部分不被编造数据污染。两者结合,才是"文献不编、数据不编"的完整闭环。
|
||||||
Binary file not shown.
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
output
|
||||||
|
*.pyc
|
||||||
56
references/uav_aeromagnetic_compensation/README.md
Normal file
56
references/uav_aeromagnetic_compensation/README.md
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
# UAV aeromagnetic compensation — literature archive
|
||||||
|
|
||||||
|
Archived: 2026-07-20
|
||||||
|
Verified entries: 30
|
||||||
|
PDFs bundled: 3
|
||||||
|
|
||||||
|
Every entry in `references.bib` passed independent verification (arXiv ID / DOI resolution and/or cross-source title match, similarity >= 0.9) via the literature-search-verify skill before being archived here. Citation keys follow the surname+year convention and are stable -- the paper-writing-grounded skill's `\cite{}` calls should match these keys directly.
|
||||||
|
|
||||||
|
## Entries (chronological, oldest first)
|
||||||
|
|
||||||
|
- **leach1980** (1980) — Aeromagnetic Compensation as a Linear Regression Problem. *Information Linkage Between Applied Mathematics and Industry*. DOI: 10.1016/b978-0-12-628750-9.50017-6
|
||||||
|
- Note: Early foundational formulation of aeromagnetic compensation as a linear regression / least-squares problem
|
||||||
|
- **williams1993** (1993) — Aeromagnetic compensation using neural networks. *Neural Computing \& Applications*. DOI: 10.1007/bf01414949
|
||||||
|
- **leblanc2001** (2001) — Denoising of aeromagnetic data via the wavelet transform. *Geophysics*. DOI: 10.1190/1.1487121
|
||||||
|
- **fedi2006** (2006) — On ``Wavelet denoising of aeromagnetic data'' (George E. Leblanc and William A. Morris, 2001, Geophysics, 71, 1793--1804). *Geophysics*. DOI: 10.1190/1.2233897
|
||||||
|
- Note: Discussion/comment on Leblanc \& Morris 2001
|
||||||
|
- **zhang2011** (2011) — A simplified aeromagnetic compensation model for low magnetism UAV platform. *2011 IEEE International Geoscience and Remote Sensing Symposium (IGARSS)*. DOI: 10.1109/igarss.2011.6049950
|
||||||
|
- **metge2013** (2013) — Dynamic magnetic field compensation for micro UAV attitude estimation. *2013 International Conference on Unmanned Aircraft Systems (ICUAS)*. DOI: 10.1109/icuas.2013.6564754
|
||||||
|
- Note: Magnetic compensation for onboard attitude estimation, not for the aeromagnetic survey signal itself
|
||||||
|
- **zhang2016** (2016) — Aeromagnetic compensation with partial least square regression. *ASEG Extended Abstracts*. DOI: 10.1071/aseg2016ab300
|
||||||
|
- **zhao2016** (2016) — A Novel Aeromagnetic Compensation Method Based on the Improved Recursive Least-Squares. *Smart Innovation, Systems and Technologies*. DOI: 10.1007/978-3-319-50212-0_21
|
||||||
|
- **ma2017** (2017) — A dual estimate method for aeromagnetic compensation. *Measurement Science and Technology*. DOI: 10.1088/1361-6501/aa883b
|
||||||
|
- **wu2017** (2017) — Aeromagnetic gradient compensation method for helicopter based on \ensuremath{\epsilon}-support vector regression algorithm. *Journal of Applied Remote Sensing*. DOI: 10.1117/1.jrs.11.025012
|
||||||
|
- **li2018** (2018) — Aeromagnetic compensation of Rotor UAV Based on Least Squares. *2018 37th Chinese Control Conference (CCC)*. DOI: 10.23919/chicc.2018.8483068
|
||||||
|
- **melo2018** (2018) — 2D discrete wavelet transform for denoising aeromagnetic data. *SEG Technical Program Expanded Abstracts 2018*. DOI: 10.1190/segam2018-2998295.1
|
||||||
|
- **hang2019** (2019) — A Simulation Method of Generating the Output of Magnetometer for Aeromagnetic Compensation. *IGARSS 2019 - 2019 IEEE International Geoscience and Remote Sensing Symposium*. DOI: 10.1109/igarss.2019.8897903
|
||||||
|
- **tuck2019** (2019) — Characterization and compensation of magnetic interference resulting from unmanned aircraft systems. *Carleton University*. DOI: 10.22215/etd/2019-13546
|
||||||
|
- **walter2019** (2019) — Spectral Analysis of Magnetometer Swing in High-Resolution UAV-borne Aeromagnetic Surveys. *2019 IEEE Systems and Technologies for Remote Sensing Applications Through Unmanned Aerial Systems (STRATUS)*. DOI: 10.1109/stratus.2019.8713313
|
||||||
|
- **wang2019** (2019) — An Automatic Method to Estimate the Calibration Quality of the Aeromagnetic Compensation. *IGARSS 2019 - 2019 IEEE International Geoscience and Remote Sensing Symposium*. DOI: 10.1109/igarss.2019.8898533
|
||||||
|
- **zhao2020** (2020) — An Aeromagnetic Compensation Algorithm Based on Neural Network. *82nd EAGE Annual Conference \& Exhibition*. DOI: 10.3997/2214-4609.202010906
|
||||||
|
- **gnadt2022** (2022) — Derivation and Extensions of the Tolles-Lawson Model for Aeromagnetic Compensation. *arXiv preprint arXiv:2212.09899*
|
||||||
|
- **nerrise2024** (2024) — Physics-Informed Calibration of Aeromagnetic Compensation in Magnetic Navigation Systems using Liquid Time-Constant Networks. *arXiv preprint arXiv:2401.09631*
|
||||||
|
- **yuan2024** (2024) — Application study of UAV aeromagnetic measurement based on rubidium optical pump magnetometer. *International Workshop on Gravity, Electrical \& Magnetic Methods and Their Applications, Shenzhen, China, May 19--22, 2024*. DOI: 10.1190/gem2024-021.1
|
||||||
|
- **dai2025** (2025) — Aeromagnetic Compensation for UAVs Using Transformer Neural Networks. *Sensors*. DOI: 10.3390/s25226852
|
||||||
|
- **fang2025** (2025) — An aeromagnetic compensation method based on the extended Tolles Lawson model. *Journal of Physics: Conference Series*. DOI: 10.1088/1742-6596/3169/1/012043
|
||||||
|
- **hu2025** (2025) — Influence of Attitude Changes on Magnetic Measurement Accuracy in UAV Magnetic Anomaly Detection. *2025 5th International Conference on Sensors and Information Technology (ICSI)*. DOI: 10.1109/icsi64877.2025.11009300
|
||||||
|
- **qiao2025** (2025) — Dual-Channel Aeromagnetic Compensation Method for Continuous and Intermittent OBE Interference. *IEEE Transactions on Instrumentation and Measurement*. DOI: 10.1109/tim.2025.3599271
|
||||||
|
- **wang2025** (2025) — An Aeromagnetic Compensation Algorithm Based on a Temporal Convolutional Network. *Applied Sciences*. DOI: 10.3390/app15063105
|
||||||
|
- **wang2025maneuver** (2025) — Magnetometer Compensation for Magnetic Interference in Aircraft Maneuvers by Using INS. *Advances in Guidance, Navigation and Control*. DOI: 10.1007/978-981-96-2240-5_29
|
||||||
|
- **song2026** (2026) — An Enhanced Tolles--Lawson Model With Temperature Compensation for Aeromagnetic Compensation of Triaxial Magnetometers. *IEEE Transactions on Instrumentation and Measurement*. DOI: 10.1109/tim.2026.3697092
|
||||||
|
- **sun2026** (2026) — Physics-Informed Tolles--Lawson and Neural Network Hybrid Modeling for Magnetic Compensation in Uncrewed Ground Vehicles. *IEEE Sensors Journal*. DOI: 10.1109/jsen.2026.3684910
|
||||||
|
- Note: Ground-vehicle (not airborne UAV) application of the Tolles--Lawson + NN hybrid compensation approach
|
||||||
|
- **xie2026** (2026) — Aeromagnetic Nonlinear Interference Compensation Method Based on Hybrid LSTM and BP Neural Network Architecture. *Computer Science and Application*. DOI: 10.12677/csa.2026.161011
|
||||||
|
- **you2026** (2026) — Electromagnetic interference compensation for aeromagnetic data using adaptive wavelet denoising and partial least squares regression. *Measurement Science and Technology*. DOI: 10.1088/1361-6501/ae8616
|
||||||
|
|
||||||
|
## Flagged during search — NOT included above, do not cite
|
||||||
|
|
||||||
|
- An Aeromagnetic Compensation Algorithm based on Complete Ensemble Empirical Mode Decomposition with Adaptive Noise and a Physics-Guided Neural Network (DOI 10.52710/fcb.145) — DOI resolves and title matches via Crossref, but the venue is "Fuel Cells Bulletin", topically unrelated to aeromagnetics -- likely a hijacked/predatory journal or metadata error. Do not cite.
|
||||||
|
|
||||||
|
## Search coverage notes
|
||||||
|
|
||||||
|
Direction covered: UAV/airborne aeromagnetic compensation (Tolles-Lawson family) -- theory, least-squares/ridge/PLS regression variants, wavelet denoising, neural-network (BP/LSTM/TCN/Transformer/physics-informed) approaches, and UAV-specific magnetic-interference characterization.
|
||||||
|
Queried via arXiv + Crossref + Semantic Scholar (S2 was rate-limited (HTTP 429) for much of the session, so most entries only got single-channel verification -- arXiv ID or DOI resolution -- rather than the additional cross-source title check; this is noted per-entry as "unverified"/"skipped" in the raw JSON reports, not silently upgraded to double-verified).
|
||||||
|
Searched but found no on-topic hits: Kalman-filter-based aeromagnetic compensation; genetic-algorithm/PSO-based aeromagnetic compensation.
|
||||||
|
Not covered at all: CNKI/Wanfang/VIP (Chinese databases, no public API) -- use the Zotero Connector browser extension logged into a university account for these.
|
||||||
|
Raw per-query JSON search/verification reports (including filtered-out noise from ambiguous keyword matches like "Tolles"/"Lawson" as surnames) are kept in .claude/skills/literature-search-verify/output/ for audit purposes and are not part of this archive.
|
||||||
BIN
references/uav_aeromagnetic_compensation/pdfs/fang2025_tlgf.pdf
Normal file
BIN
references/uav_aeromagnetic_compensation/pdfs/fang2025_tlgf.pdf
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
251
references/uav_aeromagnetic_compensation/references.bib
Normal file
251
references/uav_aeromagnetic_compensation/references.bib
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
% UAV aeromagnetic compensation -- verified references, chronological order
|
||||||
|
% Archived 2026-07-20
|
||||||
|
|
||||||
|
@article{leach1980,
|
||||||
|
title = {Aeromagnetic Compensation as a Linear Regression Problem},
|
||||||
|
author = {Leach, Barrie W.},
|
||||||
|
year = {1980},
|
||||||
|
journal = {Information Linkage Between Applied Mathematics and Industry},
|
||||||
|
doi = {10.1016/b978-0-12-628750-9.50017-6},
|
||||||
|
note = {Early foundational formulation of aeromagnetic compensation as a linear regression / least-squares problem}
|
||||||
|
}
|
||||||
|
|
||||||
|
@article{williams1993,
|
||||||
|
title = {Aeromagnetic compensation using neural networks},
|
||||||
|
author = {Williams, Peter M.},
|
||||||
|
year = {1993},
|
||||||
|
journal = {Neural Computing \& Applications},
|
||||||
|
doi = {10.1007/bf01414949}
|
||||||
|
}
|
||||||
|
|
||||||
|
@article{leblanc2001,
|
||||||
|
title = {Denoising of aeromagnetic data via the wavelet transform},
|
||||||
|
author = {Leblanc, George E. and Morris, William A.},
|
||||||
|
year = {2001},
|
||||||
|
journal = {Geophysics},
|
||||||
|
doi = {10.1190/1.1487121}
|
||||||
|
}
|
||||||
|
|
||||||
|
@article{fedi2006,
|
||||||
|
title = {On ``Wavelet denoising of aeromagnetic data'' (George E. Leblanc and William A. Morris, 2001, Geophysics, 71, 1793--1804)},
|
||||||
|
author = {Fedi, M. and Quarta, T.},
|
||||||
|
year = {2006},
|
||||||
|
journal = {Geophysics},
|
||||||
|
doi = {10.1190/1.2233897},
|
||||||
|
note = {Discussion/comment on Leblanc \& Morris 2001}
|
||||||
|
}
|
||||||
|
|
||||||
|
@inproceedings{zhang2011,
|
||||||
|
title = {A simplified aeromagnetic compensation model for low magnetism UAV platform},
|
||||||
|
author = {Zhang, Baogang and Guo, Ziqi and Qiao, Yanchao},
|
||||||
|
year = {2011},
|
||||||
|
booktitle = {2011 IEEE International Geoscience and Remote Sensing Symposium (IGARSS)},
|
||||||
|
doi = {10.1109/igarss.2011.6049950}
|
||||||
|
}
|
||||||
|
|
||||||
|
@inproceedings{metge2013,
|
||||||
|
title = {Dynamic magnetic field compensation for micro UAV attitude estimation},
|
||||||
|
author = {Metge, J. and Megret, R. and Giremus, A. and Berthoumieu, Y. and Mazel, C.},
|
||||||
|
year = {2013},
|
||||||
|
booktitle = {2013 International Conference on Unmanned Aircraft Systems (ICUAS)},
|
||||||
|
doi = {10.1109/icuas.2013.6564754},
|
||||||
|
note = {Magnetic compensation for onboard attitude estimation, not for the aeromagnetic survey signal itself}
|
||||||
|
}
|
||||||
|
|
||||||
|
@article{zhang2016,
|
||||||
|
title = {Aeromagnetic compensation with partial least square regression},
|
||||||
|
author = {Zhang, Dailei and Huang, Danian and Lu, Junwei and Zhu, Boyuan},
|
||||||
|
year = {2016},
|
||||||
|
journal = {ASEG Extended Abstracts},
|
||||||
|
doi = {10.1071/aseg2016ab300}
|
||||||
|
}
|
||||||
|
|
||||||
|
@incollection{zhao2016,
|
||||||
|
title = {A Novel Aeromagnetic Compensation Method Based on the Improved Recursive Least-Squares},
|
||||||
|
author = {Zhao, Guanyi and Shao, Yuqing and Han, Qi and Tong, Xiaojun},
|
||||||
|
year = {2016},
|
||||||
|
booktitle = {Smart Innovation, Systems and Technologies},
|
||||||
|
doi = {10.1007/978-3-319-50212-0_21}
|
||||||
|
}
|
||||||
|
|
||||||
|
@article{ma2017,
|
||||||
|
title = {A dual estimate method for aeromagnetic compensation},
|
||||||
|
author = {Ma, Ming and Zhou, Zhijian and Cheng, Defu},
|
||||||
|
year = {2017},
|
||||||
|
journal = {Measurement Science and Technology},
|
||||||
|
doi = {10.1088/1361-6501/aa883b}
|
||||||
|
}
|
||||||
|
|
||||||
|
@article{wu2017,
|
||||||
|
title = {Aeromagnetic gradient compensation method for helicopter based on \ensuremath{\epsilon}-support vector regression algorithm},
|
||||||
|
author = {Wu, Peilin and Zhang, Qunying and Fei, Chunjiao and Fang, Guangyou},
|
||||||
|
year = {2017},
|
||||||
|
journal = {Journal of Applied Remote Sensing},
|
||||||
|
doi = {10.1117/1.jrs.11.025012}
|
||||||
|
}
|
||||||
|
|
||||||
|
@inproceedings{li2018,
|
||||||
|
title = {Aeromagnetic compensation of Rotor UAV Based on Least Squares},
|
||||||
|
author = {Li, Han and Ge, Jian and Dong, Haobin and Qiu, Xiangyu and Luo, Wang and Liu, Huan and Yuan, Zhiwen and Zhu, Jun and Zhang, Haiyang},
|
||||||
|
year = {2018},
|
||||||
|
booktitle = {2018 37th Chinese Control Conference (CCC)},
|
||||||
|
doi = {10.23919/chicc.2018.8483068}
|
||||||
|
}
|
||||||
|
|
||||||
|
@inproceedings{melo2018,
|
||||||
|
title = {2D discrete wavelet transform for denoising aeromagnetic data},
|
||||||
|
author = {Melo, Felipe F. and Barbosa, Val{\'e}ria C. F. and Jim{\'e}nez-Teja, Yolanda},
|
||||||
|
year = {2018},
|
||||||
|
booktitle = {SEG Technical Program Expanded Abstracts 2018},
|
||||||
|
doi = {10.1190/segam2018-2998295.1}
|
||||||
|
}
|
||||||
|
|
||||||
|
@inproceedings{hang2019,
|
||||||
|
title = {A Simulation Method of Generating the Output of Magnetometer for Aeromagnetic Compensation},
|
||||||
|
author = {Hang, Zhiyuan and He, Futong and Wang, Zhifang and Han, Qi},
|
||||||
|
year = {2019},
|
||||||
|
booktitle = {IGARSS 2019 - 2019 IEEE International Geoscience and Remote Sensing Symposium},
|
||||||
|
doi = {10.1109/igarss.2019.8897903}
|
||||||
|
}
|
||||||
|
|
||||||
|
@phdthesis{tuck2019,
|
||||||
|
title = {Characterization and compensation of magnetic interference resulting from unmanned aircraft systems},
|
||||||
|
author = {Tuck, Loughlin},
|
||||||
|
year = {2019},
|
||||||
|
school = {Carleton University},
|
||||||
|
doi = {10.22215/etd/2019-13546}
|
||||||
|
}
|
||||||
|
|
||||||
|
@inproceedings{walter2019,
|
||||||
|
title = {Spectral Analysis of Magnetometer Swing in High-Resolution UAV-borne Aeromagnetic Surveys},
|
||||||
|
author = {Walter, Callum and Braun, Alexander and Fotopoulos, Georgia},
|
||||||
|
year = {2019},
|
||||||
|
booktitle = {2019 IEEE Systems and Technologies for Remote Sensing Applications Through Unmanned Aerial Systems (STRATUS)},
|
||||||
|
doi = {10.1109/stratus.2019.8713313}
|
||||||
|
}
|
||||||
|
|
||||||
|
@inproceedings{wang2019,
|
||||||
|
title = {An Automatic Method to Estimate the Calibration Quality of the Aeromagnetic Compensation},
|
||||||
|
author = {Wang, Yizhen and Han, Qi and Hu, Kai and Zhan, Dechen},
|
||||||
|
year = {2019},
|
||||||
|
booktitle = {IGARSS 2019 - 2019 IEEE International Geoscience and Remote Sensing Symposium},
|
||||||
|
doi = {10.1109/igarss.2019.8898533}
|
||||||
|
}
|
||||||
|
|
||||||
|
@inproceedings{zhao2020,
|
||||||
|
title = {An Aeromagnetic Compensation Algorithm Based on Neural Network},
|
||||||
|
author = {Zhao, X. and Yu, P. and Jiao, J.},
|
||||||
|
year = {2020},
|
||||||
|
booktitle = {82nd EAGE Annual Conference \& Exhibition},
|
||||||
|
doi = {10.3997/2214-4609.202010906}
|
||||||
|
}
|
||||||
|
|
||||||
|
@article{gnadt2022,
|
||||||
|
title = {Derivation and Extensions of the Tolles-Lawson Model for Aeromagnetic Compensation},
|
||||||
|
author = {Gnadt, Albert R. and Wollaber, Allan B. and Nielsen, Aaron P.},
|
||||||
|
year = {2022},
|
||||||
|
journal = {arXiv preprint arXiv:2212.09899},
|
||||||
|
eprint = {2212.09899},
|
||||||
|
archivePrefix = {arXiv},
|
||||||
|
url = {https://arxiv.org/abs/2212.09899}
|
||||||
|
}
|
||||||
|
|
||||||
|
@article{nerrise2024,
|
||||||
|
title = {Physics-Informed Calibration of Aeromagnetic Compensation in Magnetic Navigation Systems using Liquid Time-Constant Networks},
|
||||||
|
author = {Nerrise, Favour and Sosanya, Andrew Sosa and Neary, Patrick},
|
||||||
|
year = {2024},
|
||||||
|
journal = {arXiv preprint arXiv:2401.09631},
|
||||||
|
eprint = {2401.09631},
|
||||||
|
archivePrefix = {arXiv},
|
||||||
|
url = {https://arxiv.org/abs/2401.09631}
|
||||||
|
}
|
||||||
|
|
||||||
|
@inproceedings{yuan2024,
|
||||||
|
title = {Application study of UAV aeromagnetic measurement based on rubidium optical pump magnetometer},
|
||||||
|
author = {Yuan, Peng and Qiao, Zhong-kun},
|
||||||
|
year = {2024},
|
||||||
|
booktitle = {International Workshop on Gravity, Electrical \& Magnetic Methods and Their Applications, Shenzhen, China, May 19--22, 2024},
|
||||||
|
doi = {10.1190/gem2024-021.1}
|
||||||
|
}
|
||||||
|
|
||||||
|
@article{dai2025,
|
||||||
|
title = {Aeromagnetic Compensation for UAVs Using Transformer Neural Networks},
|
||||||
|
author = {Dai, Weiming and Yang, Changcheng and Zhou, Shuai},
|
||||||
|
year = {2025},
|
||||||
|
journal = {Sensors},
|
||||||
|
doi = {10.3390/s25226852}
|
||||||
|
}
|
||||||
|
|
||||||
|
@article{fang2025,
|
||||||
|
title = {An aeromagnetic compensation method based on the extended Tolles Lawson model},
|
||||||
|
author = {Fang, Yuanxing and Zhang, Chao and Zheng, Yaoxin and Liu, Weiqiang and Zha, Songyuan},
|
||||||
|
year = {2025},
|
||||||
|
journal = {Journal of Physics: Conference Series},
|
||||||
|
doi = {10.1088/1742-6596/3169/1/012043}
|
||||||
|
}
|
||||||
|
|
||||||
|
@inproceedings{hu2025,
|
||||||
|
title = {Influence of Attitude Changes on Magnetic Measurement Accuracy in UAV Magnetic Anomaly Detection},
|
||||||
|
author = {Hu, Xinyue},
|
||||||
|
year = {2025},
|
||||||
|
booktitle = {2025 5th International Conference on Sensors and Information Technology (ICSI)},
|
||||||
|
doi = {10.1109/icsi64877.2025.11009300}
|
||||||
|
}
|
||||||
|
|
||||||
|
@article{qiao2025,
|
||||||
|
title = {Dual-Channel Aeromagnetic Compensation Method for Continuous and Intermittent OBE Interference},
|
||||||
|
author = {Qiao, Zhi and Li, You and Meng, Zhaohai and Han, Qi},
|
||||||
|
year = {2025},
|
||||||
|
journal = {IEEE Transactions on Instrumentation and Measurement},
|
||||||
|
doi = {10.1109/tim.2025.3599271}
|
||||||
|
}
|
||||||
|
|
||||||
|
@article{wang2025,
|
||||||
|
title = {An Aeromagnetic Compensation Algorithm Based on a Temporal Convolutional Network},
|
||||||
|
author = {Wang, Han and Zuo, Boxin},
|
||||||
|
year = {2025},
|
||||||
|
journal = {Applied Sciences},
|
||||||
|
doi = {10.3390/app15063105}
|
||||||
|
}
|
||||||
|
|
||||||
|
@incollection{wang2025maneuver,
|
||||||
|
title = {Magnetometer Compensation for Magnetic Interference in Aircraft Maneuvers by Using INS},
|
||||||
|
author = {Wang, Guanjie and Yue, Yazhou and Dong, Jiahang and Zhou, Qi and Wang, Haoming and Wang, Jingjiang and Jiang, Haofeng},
|
||||||
|
year = {2025},
|
||||||
|
booktitle = {Advances in Guidance, Navigation and Control},
|
||||||
|
series = {Lecture Notes in Electrical Engineering},
|
||||||
|
doi = {10.1007/978-981-96-2240-5_29}
|
||||||
|
}
|
||||||
|
|
||||||
|
@article{song2026,
|
||||||
|
title = {An Enhanced Tolles--Lawson Model With Temperature Compensation for Aeromagnetic Compensation of Triaxial Magnetometers},
|
||||||
|
author = {Song, Wenhua and Yang, Zhicheng and Ma, Yan and Li, Bin and Xie, Songyun and Chen, Chen and Qi, Kankan},
|
||||||
|
year = {2026},
|
||||||
|
journal = {IEEE Transactions on Instrumentation and Measurement},
|
||||||
|
doi = {10.1109/tim.2026.3697092}
|
||||||
|
}
|
||||||
|
|
||||||
|
@article{sun2026,
|
||||||
|
title = {Physics-Informed Tolles--Lawson and Neural Network Hybrid Modeling for Magnetic Compensation in Uncrewed Ground Vehicles},
|
||||||
|
author = {Sun, Zhaolong and Zhang, Yiwen and Deng, Shengyao and Xiao, Liang and Liu, Xin and Zhang, Yang},
|
||||||
|
year = {2026},
|
||||||
|
journal = {IEEE Sensors Journal},
|
||||||
|
doi = {10.1109/jsen.2026.3684910},
|
||||||
|
note = {Ground-vehicle (not airborne UAV) application of the Tolles--Lawson + NN hybrid compensation approach}
|
||||||
|
}
|
||||||
|
|
||||||
|
@article{xie2026,
|
||||||
|
title = {Aeromagnetic Nonlinear Interference Compensation Method Based on Hybrid LSTM and BP Neural Network Architecture},
|
||||||
|
author = {Xie, Jihong},
|
||||||
|
year = {2026},
|
||||||
|
journal = {Computer Science and Application},
|
||||||
|
doi = {10.12677/csa.2026.161011}
|
||||||
|
}
|
||||||
|
|
||||||
|
@article{you2026,
|
||||||
|
title = {Electromagnetic interference compensation for aeromagnetic data using adaptive wavelet denoising and partial least squares regression},
|
||||||
|
author = {You, Gaoyun and Li, Xinsan and Li, Ting and Li, Can and Shen, Qiang},
|
||||||
|
year = {2026},
|
||||||
|
journal = {Measurement Science and Technology},
|
||||||
|
doi = {10.1088/1361-6501/ae8616}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user