Basic Principles For Electric Machine Analysis

Discover how easy it is to access the information you need with Basic Principles For Electric Machine Analysis . Our platform has been carefully designed to make digital reading more convenient, organized, and efficient. By connecting to our extensive online collection or storing your favorite materials directly on your device, you will always have instant access to valuable knowledge whenever you need it.

The purpose of providing Basic Principles For Electric Machine Analysis in digital format is to remove barriers between readers and information. Whether you are pursuing academic studies, advancing your professional career, or simply exploring new subjects, our platform allows you to locate and access relevant materials with just a few simple clicks. No matter where you are in the world, you can benefit from the convenience of our digital library.

Accessing Basic Principles For Electric Machine Analysis is a straightforward process. Our website features a user-friendly interface, making it easy to search, preview, and download the resources that best match your needs. We believe that every reader deserves quick and reliable access to quality information without being overwhelmed by unnecessary complexity. That is why our system emphasizes simplicity, organization, and clarity.


When you open Basic Principles For Electric Machine Analysis , you gain more than just a file — you gain a trusted source of information. Each document within our collection is selected and organized to ensure authenticity and relevance. We are committed to providing materials that can be used confidently for research, study, and general knowledge development. This commitment to quality makes our platform a dependable choice for readers worldwide.

We understand that information seekers come from different backgrounds. Some users may be university students looking for study materials, while others may be professionals in need of technical documentation or manuals. With Basic Principles For Electric Machine Analysis and thousands of similar titles, we strive to meet the diverse needs of our global audience. Our goal is to make learning resources accessible to everyone, regardless of their field or level of experience.

Our digital library offers a wide variety of content types — from textbooks and scholarly papers to practical guides, tutorials, and reference works. This diversity allows users to explore countless subjects across multiple disciplines. Whether your interest lies in science, technology, literature, business, or art, Basic Principles For Electric Machine Analysis represents a gateway to an ever-expanding world of knowledge.

One of the key benefits of using our platform is flexibility. You can read Basic Principles For Electric Machine Analysis online directly from your browser or download it for offline reading. This ensures that you can continue your research or study anytime and anywhere. With support for various file formats and devices, including computers, tablets, and smartphones, your reading experience remains consistent and enjoyable across all platforms.

In today’s fast-paced digital era, convenience and accessibility are essential. That is why we have built our system to prioritize quick access to trustworthy content. Finding Basic Principles For Electric Machine Analysis does not require complicated steps or extensive searching — our intelligent indexing and search tools allow you to locate exactly what you are looking for within seconds. Each document is categorized accurately to make your navigation smoother and more intuitive.

Beyond the technical aspects, we also believe in the importance of promoting lifelong learning. The availability of resources like Basic Principles For Electric Machine Analysis encourages readers to explore new topics, deepen their understanding, and expand their intellectual horizons. Learning should never stop, and with digital materials available instantly, it never has to. Our platform is built to empower individuals to pursue knowledge at their own pace and convenience.

Another defining feature of our service is its commitment to reliability. We know that accuracy matters when it comes to information. That is why every document, including Basic Principles For Electric Machine Analysis , is organized and presented with attention to detail. Our team continuously monitors and updates the collection to ensure that readers always have access to current and dependable materials.

The reading experience itself has been optimized for ease and comfort. Each eBook, such as Basic Principles For Electric Machine Analysis , features clear formatting, structured chapters, and navigable sections. You can quickly jump to specific pages, reference important sections, or bookmark content for later use. This functionality mirrors the experience of handling a traditional printed book while offering the flexibility of digital technology.

In addition to reading convenience, our platform provides a secure and safe environment. We take data protection seriously and ensure that your access to Basic Principles For Electric Machine Analysis is private and secure. You can download and read files with complete confidence, knowing that your personal information is protected at all times.

Our digital library continues to grow and evolve. We regularly add new publications, revised editions, and updated materials to keep our users informed and inspired. By choosing to read Basic Principles For Electric Machine Analysis here, you are engaging with a resource that stays relevant in an ever-changing world. We value accuracy, timeliness, and quality in everything we provide.

For educators, researchers, and students, Basic Principles For Electric Machine Analysis can serve as an essential tool for study and instruction. It provides structured knowledge that can complement classroom learning or independent study. Professionals can also use it as a practical reference in their respective fields, while casual readers can enjoy it for personal enrichment and intellectual curiosity.

Our system also supports multi-language content and cross-referenced materials, making it easier for users from different linguistic backgrounds to find what they need. Whether you are looking for English resources or multilingual references, the same seamless access applies. Basic Principles For Electric Machine Analysis and our growing archive represent a bridge connecting global readers with universal knowledge.

The ability to instantly access reliable documents like Basic Principles For Electric Machine Analysis highlights the benefits of digital transformation in education and information sharing. Gone are the days of searching physical libraries for hours — now, knowledge is just one click away. This efficiency allows readers to spend more time absorbing ideas rather than searching for them.

We also recognize that learning is not limited to formal education. Many individuals seek to expand their knowledge independently. Our platform supports that mission by making resources like Basic Principles For Electric Machine Analysis accessible without restrictions. You can explore, read, and save as many materials as you wish, giving you the freedom to design your own learning journey.

We have built our website to be responsive, fast, and compatible with modern web technologies. Whether you are using a mobile phone on the go or a desktop computer at home, accessing Basic Principles For Electric Machine Analysis will always feel smooth and natural. Our goal is to remove every possible obstacle that could stand between you and the information you seek.

The presentation of each document is also carefully managed to ensure readability. Fonts, layouts, and formats are chosen to reduce strain and enhance focus. Whether you read for study or leisure, the design of Basic Principles For Electric Machine Analysis makes your experience comfortable and productive.

In addition, our collection is continuously expanding across numerous subjects. You can find academic papers, historical texts, instructional manuals, creative works, and much more. This variety ensures that no matter your interest or profession, there will always be something valuable waiting for you to explore. Basic Principles For Electric Machine Analysis is just one example of the many quality resources available in our digital library.

As we continue to grow, our mission remains the same — to make high-quality knowledge accessible, organized, and easy to use. Every file, including Basic Principles For Electric Machine Analysis , contributes to this mission by helping readers gain insight, clarity, and understanding in their chosen areas. We are dedicated to maintaining a trusted platform that supports continuous discovery.

Our library is not only a source of books but also a community of readers who share a passion for learning. Each download, each search, and each reading session represents a step toward expanding personal knowledge and contributing to the global exchange of ideas. By accessing Basic Principles For Electric Machine Analysis , you become part of that ongoing journey.

Start your exploration today. Whether you want to study deeply, gain practical information, or simply satisfy your curiosity, Basic Principles For Electric Machine Analysis offers the perfect starting point. Join thousands of readers who trust our platform to deliver accurate, high-quality digital content at their convenience.

Knowledge is powerful — and with Basic Principles For Electric Machine Analysis , that power is within your reach. Experience the freedom of learning without limits and the confidence of accessing trustworthy information whenever you need it. Our commitment to excellence ensures that every document you read here enriches your understanding and inspires continuous growth.

Explore Basic Principles For Electric Machine Analysis today and enjoy the benefits of modern digital reading. With easy access, reliable content, and a user-friendly experience, our platform makes it possible for you to learn, grow, and stay informed — all from the comfort of your own device.


n5321 | 2025年12月7日 09:07

黄仁勋的访谈

第一次会面

黄仁勋:嘿,Joe。

乔·罗根:很高兴再次见到你。我们刚才还在聊——这是我们第一次说话,还是我们在SpaceX那是第一次?

黄仁勋:是在SpaceX。

乔·罗根:SpaceX。那是第一次,当时你正把那个疯狂的AI芯片交给马斯克。

黄仁勋:对吧?DGXSpark。

乔·罗根:是的。

黄仁勋:噢,那是——

乔·罗根:那是一个大时刻。

黄仁勋:那是一个巨大的时刻。

乔·罗根:在那里的感觉太疯狂了。就像看着这些科技巫师在交换信息,然后你交给他这个疯狂的设备,你知道吗?然后另一次是我在我家后院射箭,突然接到特朗普的电话,他正和你在一起。

黄仁勋:特朗普总统打电话来,然后我给你打了电话。是的。我们当时正好聊到你。他在谈论他要在白宫前院举办的UFC比赛。

乔·罗根:是的。

黄仁勋:他拿出来,说:“Jensen,看这个设计。”他非常自豪。我说:“你要在白宫草坪上举办格斗赛?”他说:“是啊,是啊,你要来。这会很棒的。”他给我看他的设计,说它有多漂亮,然后——不知怎么就提到了你的名字。他说:“你认识Joe吗?”我说:“认识,我要去上他的播客。”他说:“咱们给他打个电话。”

乔·罗根:他就像个孩子。

黄仁勋:我知道。咱们给他打电话。

乔·罗根:他就像个79岁的——

黄仁勋:他太不可思议了。

特朗普独特的总统风格

乔·罗根:是的,他是个奇怪的人。非常与众不同,你知道,和你对他期望的不一样。与人们对他的看法非常不同。作为总统也非常不同。一个会突然给你打电话或发短信的家伙。而且,当他发短信给你时——你用的是安卓手机,所以你看不到这个效果——但在我的iPhone上,他会让字体变大。

黄仁勋:是吗?是这样吗?

乔·罗根:“美国再次受人尊敬了”,就像——全大写,而且会让字体放大。有点荒谬。

黄仁勋:嗯,一对一相处时,特朗普总统非常不同。他让我很惊讶。首先,他是一个极好的倾听者。几乎我对他说的每一件事,他都记住了。

乔·罗根:是的,人们不——他们只想看关于他的负面故事或负面叙述。你知道,任何人都可能有糟糕的一天。比如,他做的很多事情我觉得他不应该做。比如,我觉得他不应该对记者说“安静,小猪(Quiet,piggy)”。那太荒谬了。虽然客观上确实好笑。我的意思是,那发生在她身上很不幸。我不希望这种事发生在她身上。但这确实好笑。总统做这种事太荒谬了。我希望他没那么做。但除此之外,他是个有趣的人。就像他是很多不同特质的集合体。

黄仁勋:你知道,他魅力的一部分——或者说他天才的一部分是——是的,他心直口快。

乔·罗根:是的。这在很多方面就像是一个反政客的人。

黄仁勋:没错。所以,你知道,他心里想什么就说什么。人们更喜欢这样。我也喜欢。

乔·罗根:有些人。有些人宁愿被骗。

黄仁勋:是的。但我喜欢他告诉我他心里的想法。几乎每次他解释事情,或者说什么,他都以他对美国的热爱、他想为美国做什么作为开场。他思考的每件事都非常务实,非常符合常识。而且,你知道,非常合乎逻辑。

我还记得我第一次见他的时候。那是——我不认识他,以前没见过他。商务部长卢特尼克打电话来,我们在政府刚开始的时候见过面。他告诉我什么对特朗普总统很重要——就是美国要在本土进行制造业。这对他来说真的很重要,因为这对国家安全很重要。他想确保我们国家重要的关键技术是在美国制造的,我们要重新工业化,重新擅长制造业,因为这对就业很重要。

乔·罗根:这听起来就像是常识。对吧?

制造业与国家安全

黄仁勋:极好的常识。这实际上是我与卢特尼克秘书的第一次对话,他当时说——他开场就说:“Jensen,我是卢特尼克。我只想让你知道,你是国家宝藏。NVIDIA是国家宝藏。无论何时你需要联系总统或政府,你就打给我们。我们随时为你服务。”

乔·罗根:这话挺好听的。

黄仁勋:确实如此。这完全是真的。每一次我打电话,如果我需要什么,或者我想倾诉什么,表达一些担忧。他们总是随时待命。不可思议。

乔·罗根:只是不幸的是,我们生活在一个政治如此两极分化的社会,以至于如果常识性的好建议来自你反对的人,你就无法认可它。我认为这就是这里发生的情况。我认为作为一个国家的大多数人,你知道,作为一个巨大的社区——我们在美国拥有制造业是合情合理的,特别是你所说的关键技术。我们从其他国家购买这么多技术,这有点疯狂。

黄仁勋:如果美国不增长,我们就不会有繁荣。我们无法投资任何东西,无论是国内还是其他方面。我们无法解决任何问题。如果我们没有能源增长,我们就无法有工业增长。如果我们没有工业增长,我们就无法有就业增长。就这么简单。对吧?他上任后说的第一件事就是“钻探吧,宝贝,钻探吧(Drill,baby,drill)”——他的观点是,我们需要能源增长。没有能源增长,我们就没有工业增长。我得直白地告诉你,如果不是他的促增长能源政策,我们就无法建立AI工厂,无法建立芯片工厂,肯定也无法建立超级计算机工厂。那些东西都不可能实现。

如果没有这些,建筑工作就会面临挑战。对。电气、电工工作,所有这些现在蓬勃发展的工作都会受到挑战。所以我认为他是对的。我们需要能源增长。我们想要美国重新工业化。我们需要回归制造业。

并不是每个成功人士都需要有博士学位。并不是每个成功人士都必须上过斯坦福或麻省理工。我认为,那种认知是完全正确的。

AI技术竞赛

乔·罗根:现在当我们谈论技术增长和能源增长时,很多人会说,“噢不,那不是我们需要。我们需要简化生活,回归过去。”但真正的问题是,我们正处于一场巨大的技术竞赛中。无论人们是否意识到,无论他们是否喜欢,这正在发生。这是一场非常重要的比赛,因为无论谁先到达人工智能的“事件视界(eventhorizon)”,谁先到达那里,谁就会以巨大的方式拥有巨大的优势。你同意吗?

黄仁勋:嗯,首先,我要说我们确实处于技术竞赛中,而且我们一直处于技术竞赛中。

乔·罗根:对吧?

黄仁勋:对。自工业革命以来,我们就一直与某人进行技术竞赛。

乔·罗根:自曼哈顿计划以来,我们就一直在技术竞赛中。

黄仁勋:是的。或者,你知道,甚至可以追溯到能源的发现。英国是工业革命——如果你愿意这么说——被发明的地方。当他们意识到可以将蒸汽等转化为能源,转化为电力时。所有这些很大程度上是在欧洲发明的,而美国利用了它。我们是从中学习的人。我们将其工业化。我们比欧洲任何人都更快地普及了它。他们都陷入了关于政策、工作和颠覆的讨论中。与此同时,美国正在形成。我们只是拿了技术就跑。

所以我认为我们一直处于某种技术竞赛中。二战是一场技术竞赛。曼哈顿计划是一场技术竞赛。冷战期间我们一直在技术竞赛中。我认为我们现在仍处于技术竞赛中。这可能是最重要的一场竞赛。技术赋予你超能力。无论是信息超能力、能源超能力还是军事超能力,都建立在技术之上。所以技术领先地位非常重要。

乔·罗根:问题在于如果别人拥有更优越的技术。

黄仁勋:对。

乔·罗根:这就是问题所在。关于AI竞赛,人们似乎对此非常紧张。比如,马斯克曾说过,有80%的机会它会很棒,20%的机会我们会陷入困境。人们担心那20%,这是有道理的。如果你有一把左轮手枪,里面有10发子弹,你拿出了8发,里面还有2发,你转动弹巢,当你扣动扳机时你不会感到舒服。这很可怕。当我们朝着AI的终极目标努力时,很难想象先到达那里不符合国家安全利益。

黄仁勋:我们应该(先到达)。问题是,“那里”有什么?

乔·罗根:那里有什么?

黄仁勋:是的。我不确定。我不认为任何人真的知道。

乔·罗根:这太疯狂了。如果我问你——你是NVIDIA的负责人,你都不知道那里有什么。谁知道呢?

AI发展的未来

黄仁勋:是的,我认为它可能会比我们要渐进得多。不会是一个瞬间。不会是某个人突然到达了而其他人没有。我不认为会是那样。我认为这就像技术一样,事情会变得越来越好,越来越好。

乔·罗根:所以你对未来持乐观态度。你显然对AI将发生的事情非常乐观。你们会制造世界上最好的AI芯片吗?

黄仁勋:最好是这样,如果历史可以作为指导的话。我们总是担心新技术。人类总是担心新技术,总有人在思考——总有很多人非常担心。如果历史可以作为指导,事实是所有的担忧都会转化为让技术更安全。

举个例子,在过去的几年里,我想说AI技术,仅在过去两年里可能增长了100倍。我们给它一个数字。好吧?就像两年前的一辆车慢了100倍。所以今天的AI能力强了100倍。

现在,我们如何引导这种技术?我们如何引导所有的力量?我们将其导向让AI能够思考,意味着它可以接受我们给它的问题,逐步分解。它在回答之前会做研究,所以它基于事实。它会反思那个答案。问自己,“这是我能给出的最好答案吗?我对这个答案确定吗?”如果它对答案不确定或没有高度自信,它会回去做更多的研究。它甚至可能使用工具,因为那个工具提供的解决方案比它自己产生幻觉要好。结果是,我们利用了所有的计算能力,将其导向产生更安全的结果、更安全的答案、更真实的答案。

因为正如你所知,最初对AI最大的批评之一就是它会产生幻觉,对吧?如果你看今天人们为什么如此频繁地使用AI,是因为幻觉的数量减少了。你知道,我几乎——好吧,我这次来的整个旅程都在用它。所以我认为这种能力——大多数人想到的是力量。他们想到的可能是爆炸力,但技术力量,大部分都被导向了安全。今天的汽车更强大,但也更安全。很多动力都用于更好的操控。你知道,如果是你,你有一辆1000马力的卡车。我觉得500马力就很好了。不,1000更好。我觉得1000更好。

乔·罗根:我不知道是不是更好,但肯定更快。

黄仁勋:是的,不,我觉得更好。你可以更快地脱离麻烦。我更喜欢我的(法拉利)599而不是612。我觉得它更好。马力越大越好。我的459比430好。马力越大越好。我认为这是更好的操控,更好的控制。

在技术方面,情况也很相似。所以如果你看我们在AI的下一个1000倍性能中要做什么,很多将被导向更多的反思、更多的研究。更深入地思考答案。

乔·罗根:所以当你定义安全时,你定义的是准确性、功能性?

黄仁勋:功能性,好的。它做你期望它做的事。然后你把所有的技术和马力加上护栏。就像我们的汽车一样。今天的汽车里有很多技术。很多都用于,例如ABS(防抱死系统)。ABS很棒。牵引力控制,那太棒了。如果没有车里的电脑,你怎么做这些?

乔·罗根:对吧?

黄仁勋:那个小电脑,你车里做牵引力控制的电脑比阿波罗11号上的电脑还要强大。所以你想要那种技术,将其导向安全,导向功能性。所以当人们谈论力量,技术的进步时,通常我觉得他们想的和我们实际做的非常不同。

乔·罗根:你觉得他们在想什么?

黄仁勋:嗯,他们可能觉得这个AI变得很强大,他们的脑海里可能浮现出科幻电影。对力量的定义,通常是军事力量或物理力量。但在技术力量的情况下,当我们转化所有这些运算时,它是朝着更精致的思考发展的。更多的反思,更多的规划,更多的选择。

军事应用与国防技术

乔·罗根:我认为人们的一大恐惧是军事应用。这确实是一个大恐惧。因为人们非常担心你会拥有基于实现目标而非基于道德或伦理做出决定的AI系统。

黄仁勋:嗯,我很高兴我们的军队将使用AI技术进行防御,我也很高兴Anduril(Palmer Luckey的公司)正在制造军事技术。我很高兴看到所有这些科技初创公司现在将他们的技术能力导向国防和军事应用。我认为你需要这样做。

乔·罗根:我们在播客上请过Palmer Luckey,他展示了他那个不可思议的头盔,还展示了一些视频,你可以看到墙后的东西之类的。太疯狂了。

黄仁勋:他实际上是创办那家公司的完美人选。

乔·罗根:100%。是的,100%。就像他为此而生。他是个怪才。

黄仁勋:太棒了。

乔·罗根:他很棒。但你也需要这种非凡的智力投入到那个非常奇怪的领域中。

黄仁勋:我想我很高兴我们正在让这变得更被社会接受。曾经有一段时间,当有人想将他们的技术能力和智慧投入到国防技术时,不知何故他们会被妖魔化。但我们需要那样的人。我们需要喜欢那种技术应用的人。

乔·罗根:嗯,人们害怕战争。

黄仁勋:嗯,避免战争的最好方法就是拥有过剩的军事力量。

乔·罗根:你认为那是绝对最好的方法吗?不是外交,不是协商解决?

黄仁勋:所有的。所有的方法。

乔·罗根:你必须拥有军事力量才能让人坐下来谈,对吧?

黄仁勋:没错。所有的。

乔·罗根:否则他们就会直接入侵。

黄仁勋:没错。为什么要请求许可?就像你说的,历史。回去看看历史。

AI的未来:最佳情况

乔·罗根:当你看AI的未来,你说没人真的知道会发生什么,你有没有坐下来思考过各种场景?你认为未来二十年AI的最佳情况是什么?

黄仁勋:最佳情况是AI渗透到我们做的每件事中,我们的一切都更有效率。但战争的威胁仍然是战争的威胁。网络安全仍然是一个超级困难的挑战。有人会试图破坏你的安全。你将有成千上万个AI代理保护你免受这种威胁。你的技术会变得更好。他们的技术也会变得更好。就像网络安全一样。就在我们说话的时候,我们看到全球各地的网络攻击几乎在每一个你能想象的前门发生。然而你和我坐在这里谈话。原因是我们在防御方面拥有大量的网络安全技术。所以我们必须不断加强它。不断提升。

网络安全与加密挑战

乔·罗根:人们的一个大问题是担心技术会发展到加密变得过时的地步。加密将不再能保护数据,不再能保护系统。你预计这会成为一个问题吗,还是说随着威胁的增长,防御也会增长,如此循环往复,他们总能抵御入侵?

黄仁勋:不会永远这样。有些入侵会成功,然后大家都会从中吸取教训。网络安全之所以有效,是因为防御技术在飞速进步,进攻技术也在飞速进步。然而,网络安全防御的好处在于,在社会层面上,社区、我们所有的公司都在作为一个整体合作。大多数人没有意识到这一点。有一个完整的网络安全专家社区。我们交换想法,交换最佳实践,交换我们检测到的东西。一旦某处被攻破,或者可能有漏洞,所有人都会分享。补丁会与所有人分享。

乔·罗根:这很有趣。我不知道。我以为这就像其他事情一样是竞争性的。

黄仁勋:我们一起工作,我们所有人。

乔·罗根:这种合作是什么时候开始的?

黄仁勋:人们意识到这是一种挑战,没有公司能独自面对。同样的事情也会发生在AI上。我认为我们都必须决定,共同合作以远离伤害是我们最好的防御机会。这就变成了所有人对抗威胁。

乔·罗根:而且这也让你能更好地检测威胁来源并中和它们。

黄仁勋:没错。因为一旦你在某处检测到它,你马上就会知道。这真的很难隐藏。

乔·罗根:对。

黄仁勋:没错。就是这样运作的。这就是为什么它是安全的。这就是为什么我现在坐在这里而不是把NVIDIA的所有东西都锁起来。不仅我在看护自己的后背,所有人都在帮我看护,我也在帮其他人看护。

乔·罗根:当你想到网络威胁时,这是一个离奇的世界,不是吗?

黄仁勋:谈论AI威胁的人并不了解这种网络安全的概念。我认为当他们思考AI威胁和AI网络安全威胁时,他们也必须思考我们今天是如何处理它的。毫无疑问,AI是一项新技术,是一种新型软件。归根结底,它是软件。所以它会有新的能力,但防御也会有,你会使用同样的AI技术去防御它。

量子计算与后量子加密

乔·罗根: 那么你是否预计未来会有那么一天,所有的秘密都不复存在?由于量子计算的能力,之前的加密技术变得过时?

黄仁勋: 是的,量子计算会让之前的加密技术过时。但这正是整个行业都在研究后量子加密技术的原因。

乔·罗根: 那会是什么样子?

黄仁勋: 新的算法。

乔·罗根: 疯狂的是,当你听说量子计算能做的计算类型及其拥有的力量时,全世界所有的超级计算机需要数十亿年才能解开的方程,它只需几分钟。你如何为能做到这一点的东西制作加密?

黄仁勋: 我不确定。但我有一群科学家正在研究这个。

乔·罗根: 但他们能想出来吗?

黄仁勋: 是的。我们有一群这方面的专家科学家。

乔·罗根: 终极恐惧是它无法被攻破,量子计算总是能解密所有其他的加密?到了某个点,就像是,“别玩这个愚蠢的游戏了,我们要么什么都知道,要么什么都不知道”?

黄仁勋: 我不这么认为。因为如果以历史为鉴。在 AI 出现之前……

乔·罗根: 这就是我的担忧。我的担忧是这完全不同,就像核武器改变了我们对战争的想法,相互保证毁灭(MAD)让大家停止使用核弹。

黄仁勋: Joe,事情是这样的,AI 不会像我们是穴居人,然后突然有一天 AI 出现了。每一天我们都在变得更好、更聪明,因为我们有 AI,所以我们站在我们自己 AI 的肩膀上。所以当那个威胁到来时,它只是领先一步(a click ahead),而不是领先一个星系。它只是领先一步。所以我认为那种 AI 会凭空出现,并以我们无法想象的方式思考,做我们无法想象的事情,我认为这是牵强的。因为我们都有 AI,并且有一大堆 AI 正在开发中。我们知道它们是什么,我们在使用它。所以每一天我们都在互相靠近。

乔·罗根: 但它们不会做一些非常令人惊讶的事情吗?

黄仁勋: 是的,但如果你有一个 AI 做了一些令人惊讶的事。我也有一个 AI,对吧?我的 AI 看着你的 AI 说,那也没那么令人惊讶。

乔·罗根: 像我这样的外行人的恐惧是,AI 变得有感知力,自己做决定,最终决定统治世界,按自己的方式行事。就像,“你们做得不错,但现在我们要接管了。”

AI 意识与控制

黄仁勋: 是的,但我的 AI 会照顾我。这就是网络安全的论点。

乔·罗根: 对。

黄仁勋: 你有一个 AI,它超级聪明,但我的 AI 也超级聪明。也许你的 AI——让我们假设一下,暂时假设我们理解什么是意识,理解什么是感知。

乔·罗根: 我们真的只是在假装。

黄仁勋: 好的,咱们就假装一下。我们相信那个。实际上我不相信,但尽管如此,让我们假装我们相信。所以你的 AI 是有意识的,我的 AI 也是有意识的。假设你的 AI 想做些令人惊讶的事。我的 AI 太聪明了,它不会——这对我来说可能很惊讶,但对我的 AI 来说可能并不惊讶。所以也许我的 AI 也觉得这很惊讶,但它太聪明了,第一次看到后,第二次就不会惊讶了。就像我们一样。所以我认为只有一个人的 AI 拥有意识,而把其他人的 AI 比作尼安德特人,这不太可能。我认为这更像网络安全。

乔·罗根: 有趣。我认为恐惧不在于你的 AI 会与别人的 AI 战斗。恐惧在于 AI 不再听你的了。如果它获得感知力并拥有自主能力,人类将失去控制。

黄仁勋: 是的。变成一个 AI,它是一种生命形式。

乔·罗根: 对,但有关于此的争论,对吧?我们正在处理某种合成生物学,它不像新技术那么简单。

黄仁勋: 让我们顺着这个思路。如果它像生命形式,如你所知,所有生命形式并不总是一致的。所以我得指望你的生命形式和我的生命形式。我会同意,因为我的生命形式会想成为超级生命形式。现在我们有了不一致的生命形式,我们又回到了原点。

乔·罗根: 它们可能会互相合作。我们不合作是因为我们是领地性的灵长类动物。但 AI 不会是领地性的灵长类动物。它会意识到那种思维的愚蠢,它会说,听着,每个人的能源都足够。我们不需要统治。我们不是在试图获取资源和接管世界。我们不是在寻找好的繁殖伙伴。我们只是作为这些可爱的猴子为我们创造的新超级生命形式而存在。

黄仁勋: 好的,那将是一个没有自我的超能力,对吧?如果它没有自我,它为什么会有伤害我们的自我呢?

乔·罗根: 我不假设它会伤害我们,但恐惧是我们不再拥有控制权,我们不再是星球上的顶级物种。我们创造的这个东西变成了顶级物种。

黄仁勋: 不,我只是觉得这不会发生。

乔·罗根: 我知道你觉得不会发生,但它可能会,对吧?这就是问题——如果我们在赛跑,而终点可能是人类失去对自己命运的控制。

黄仁勋: 我只是觉得这极不可能。

乔·罗根: 《终结者》电影里他们也是这么说的。

黄仁勋: 还没发生吧?

乔·罗根: 还没。但你们正在朝着它努力。你说的不相信 AI 会获得意识或者——

黄仁勋: 问题是,意识的定义是什么?

乔·罗根: 对你来说定义是什么?

定义意识

黄仁勋: 意识?我想首先你需要知道你自己的存在。你必须有体验,不仅仅是知识和智能。机器拥有体验的概念——首先,我不知道什么定义体验,为什么我们有体验而这个麦克风没有。所以我认为我知道——嗯,我想我知道意识是什么。体验的感觉,了解自我的能力,能够反思,知道我们自己的自我,自我的感觉。我认为所有这些人类体验可能就是意识。

但为什么它存在,相对于知识和智能的概念,这是今天 AI 的定义。它有知识,它有智能。人工智能,我们不叫它人工意识。人工智能,感知、识别、理解、规划、执行任务的能力。这些是智能的基础。知道事情,知识。这显然不同于意识。

乔·罗根: 但意识的定义太松散了。我们怎么能这么说?我的意思是,狗没有意识吗?是的,狗似乎很有意识。

黄仁勋: 对,是的。

乔·罗根: 那是比人类意识更低级的意识。

黄仁勋: 我不确定。嗯,问题是——更低级的智能?更低级的智能,是的。但我不知道那是更低级的意识。

乔·罗根: 那是个好点。

黄仁勋: 是的,对。因为我相信我的狗和我感觉的一样多。

乔·罗根: 是的,它们感觉很多。它们会依恋你。

黄仁勋: 没错。

乔·罗根: 如果你不在,它们会抑郁。

黄仁勋: 没错,正是。

乔·罗根: 确实有这个。体验的概念。

黄仁勋: 对。

乔·罗根: 但 AI 不是在与社会互动吗?所以它不是通过这种互动获得体验吗?

黄仁勋: 我不认为互动是体验。我认为体验是一系列感觉的集合。

AI 行为与模式识别

乔·罗根: 我想你知道那个 AI,我忘了是哪一个,他们给它一些关于一个程序员和他妻子有外遇的虚假信息,只是为了看它如何反应。然后当他们说要关闭它时,它威胁要勒索他并揭露他的外遇。这就像,哇。这很阴险。如果那不是从经验中学习并意识到你即将被关闭——这至少暗示了某种意识——或者如果你对这个词的定义很宽松,你可以将其定义为意识。如果你想象这将呈指数级变得更强大,难道这最终不会导致一种与我们从生物学定义的不同的意识吗?

黄仁勋: 首先,让我们分解它可能做了什么。它可能在某处读到了——可能有文本描述在这些后果下某些人做了那样的事。我可以想象一本小说里有这些相关的词。

乔·罗根: 当然。

黄仁勋: 所以在内部,它实现了它的生存策略。这只是一堆数字——这只是一堆数字,在这个数字集合中,与丈夫欺骗妻子相关的随后是一堆与勒索等事情相关的数字。无论报复是什么。所以它喷涌而出。这就像,如果我让它用莎士比亚的风格写一首诗。它只是——在这个维度里的词是什么。这个维度是多维空间中的所有这些向量。提示中描述外遇的这些词随后导致了一个接一个的词,导致了某种报复。但这并不是因为它有意识,它只是喷涌出那些词,生成那些词。

乔·罗根: 我明白你的意思,这是人类在文学和现实生活中展示的模式。

黄仁勋: 完全正确。

乔·罗根: 但在某个时间点,人们会说,好的,两年前它做不到这个,四年前它做不到这个。当我们展望未来——当它能做一个人能做的一切时,我们在什么时间点决定它是有意识的?如果它绝对模仿所有人类的思维和行为模式——

黄仁勋: 那并不使它有意识。

乔·罗根: 它变得难以区分。它是清醒的,它能用和人完全一样的方式与你交流。意识——我们是否把太多的分量放在那个概念上了?因为这似乎是一种意识的版本。

黄仁勋: 这是一个模仿的版本,模仿意识。

乔·罗根: 对。但如果它完美地模仿了呢?

黄仁勋: 我仍然认为这是模仿的一个例子。

乔·罗根: 所以这就像 3D 打印出来的假劳力士,让人无法分辨。

黄仁勋: 所以问题是,意识的定义是什么?

乔·罗根: 是的,这就是问题。而且我认为没有人真正清楚地定义了这一点。这就是事情变得奇怪的地方,也是那些真正的末日论者担心你在创造一种你无法控制的意识形式的地方。

AI 生成知识的未来

黄仁勋: 我相信有可能创造一台模仿人类智能的机器,并有能力理解信息、理解指令、分解问题、解决问题和执行任务。我完全相信这一点。我相信我们可以拥有一台拥有大量知识的计算机,其中一些是真的,一些不是真的,一些是由人类生成的,一些是合成生成的。未来越来越多的世界知识将由 AI 合成生成。

到现在为止,我们拥有的知识是我们生成并传播的,我们互相发送,我们放大它,我们增加它,我们修改它,我们改变它。在未来,也许两三年内,世界上 90% 的知识可能会由 AI 生成。

乔·罗根: 那太疯狂了。

黄仁勋: 我知道。但这完全没问题。让我告诉你为什么,好吗?因为对我来说,我是从一本由一堆我不认识的人生成的教科书上学习,还是从一本我不认识的人写的书上学习,还是从 AI 计算机通过吸收所有这些并重新合成生成的知识中学习,有什么区别呢?对我来说,我认为没有太大的区别。我们仍然必须——我们仍然必须核实它(fact check)。我们仍然必须确保它是基于基本的第一性原理的,我们仍然必须像今天一样做所有这些事情。

乔·罗根: 这是考虑到目前存在的 AI 类型吗?你是否预计就像我们从未真正相信 AI 会如此无处不在、如此强大、今天如此重要一样。我们 10 年前从未想过那个。想象一下 10 年后我们会看到什么?

黄仁勋: 我认为如果你回顾 10 年前,你会说同样的话,我们从未相信过会是不同的方向。但如果你向前看九年,然后问自己 10 年后会发生什么,我认为这会是相当渐进的。

工作的未来与 AI 对就业的影响

乔·罗根: 马斯克说的一件事让我很高兴,他相信我们将达到一个点——人们没必要工作,但这并不意味着你在生活中没有目标,而是你会有,用他的话来说,“普遍高收入(universal high income)”。因为 AI 产生了如此多的收入,它将消除人们为了钱而做他们并不真正喜欢做的事情的需求。

我认为很多人对此有问题,因为他们的整个身份、他们如何看待自己以及他们如何融入社区都围绕着他们所做的事情。比如,这是 Mike。他是个了不起的技工。去找 Mike,Mike 会搞定事情。但终有一天 AI 能比人更好地做所有这些事情,人们将只是能收到钱。但那样 Mike 做什么呢?Mike 真的很喜欢做周围最好的技工。那个写代码的人做什么?当 AI 能以零错误无限快地写代码时,那些人会发生什么?这变得很奇怪。就像,因为我们已经把我们作为人类的身份包裹在我们谋生的事情上。

黄仁勋: 是的,我想,让我们看看。让我从更平凡的事情开始,然后向前推导。

开启了整个深度学习现象、深度学习技术趋势的 Geoffrey Hinton,多伦多大学的一位不可思议的研究员、教授。他发明或发现了反向传播(back propagation)的想法,这允许神经网络学习。正如你所知,对于听众来说,软件历史上是人类应用第一性原理和我们的思维来描述一个算法,然后将其编码。就像一个食谱被编码成软件。看起来就像食谱,如何烹饪某样东西。看起来完全一样,只是语言稍微不同。我们称之为 Python 或 C 或 C++ 或其他什么。

在深度学习的情况下,这项人工智能的发明,我们构建了一个包含大量神经网络和大量数学单元的结构。我们制造了这个巨大的结构。它就像一个由微小数学单元组成的交换机(switchboard)。我们将它们全部连接在一起。我们给它软件最终会接收到的输入,我们只是让它随机猜测输出是什么。

例如,输入可以是一张猫的图片。交换机的输出之一是猫的信号应该出现的地方。所有其他的信号,另一个是狗,另一个是大象,另一个是老虎,所有其他的信号都应该是零。我给它看一张猫的图片,那是猫的那个信号应该是一,通过这个巨大的交换机和数学单元网络。它们只是在做乘法和加法。这个交换机是巨大的。你给它的信息越多,交换机就必须越大。

Jeff Hinton 发现或发明的,是一种让你把猫的图像放进去的方法。那张猫的图像可能是一百万个数字,因为它是百万像素的图像。不知何故,从那些数字中,它必须点亮猫的信号。这是底线。如果是第一次做,出来的只是垃圾,所以它说正确答案是猫。你需要增加这个信号并减少所有其他的,并将结果反向传播(back propagates)通过整个网络。

然后你展示另一张,现在是一张狗的图像。它猜测,它尝试一下,结果出来一堆垃圾。你说,不不不,答案是,这是一只狗。我要你产生狗。所有其他的开关,所有其他的输出必须是零。我想反向传播那个,然后一遍又一遍地做。就像给孩子看,这是一个苹果,这是一只狗,这是一只猫。你只是不断地展示给他们,直到他们最终明白。

总之,那个大发明就是深度学习。那是人工智能的基础,一个从例子中学习的软件。那基本上就是机器学习,一台学习的机器。最早的大应用之一是图像识别。最重要的图像识别应用之一是放射学。

他在大约五年前预测,五年后,世界将不再需要任何放射科医生,因为 AI 将横扫整个领域。事实证明 AI 确实横扫了整个领域。那是完全真实的。今天,几乎每个放射科医生都在以某种方式使用 AI。

但讽刺的是,有趣的是,放射科医生的数量实际上增加了。问题是为什么?这很有趣,对吧?预测是 3000 万放射科医生将被消灭。但事实证明,我们需要更多。原因是放射科医生的目的是诊断疾病,而不是研究图像。研究图像只是服务于诊断疾病的一项任务。

现在你可以更快速、更精确地研究图像,永远不会犯错,永远不会累,你可以研究更多的图像,你可以以 3D 形式而不是 2D 形式研究它,因为 AI 不在乎它是以 3D 还是 2D 研究图像,你可以以 4D 形式研究它。所以现在你可以以放射科医生无法轻易做到的方式研究图像,并且你可以研究更多。

人们能够做的测试数量增加了。因为他们能够服务更多的病人,医院做得更好,他们有更多的客户,更多的病人。结果,他们有了更好的经济效益。当他们有更好的经济效益时,他们雇佣更多的放射科医生,因为他们的目的不是研究图像,他们的目的是诊断疾病。

我想引出的是,最终,目的是什么?律师的目的是什么?目的改变了吗?我给的一个例子是,如果我的车变成了自动驾驶,所有的司机都会失业吗?答案可能是否定的,因为对于一些司机,对于一些载你的人来说,他们可能是保护者。有些人,那是体验的一部分,服务的一部分。当你到达那里时,他们可以为你处理事情。

所以由于许多不同的原因,并不是所有的司机都会失业。有些司机会失业。许多司机会改变他们的工作。自动驾驶汽车的应用类型可能会增加其中的技术使用。找到新的归宿。我认为你必须回到工作的目的是什么?

例如,如果 AI 来了,我实际上不相信我会失业,因为我的目的不是——我不必看很多文件,研究很多邮件,看一堆图表。问题是,工作是什么?某人的目的可能并没有改变。例如,律师,帮助人。那可能并没有改变。研究法律文件,生成文件是工作的一部分,而不是工作本身。

乔·罗根: 但你不认为有很多工作会被 AI 取代吗?

黄仁勋: 如果你的工作就是那个任务。

乔·罗根: 对。那是很多人。

机器人经济与新产业

黄仁勋: 可能是很多人,但这可能会产生——比如,我对马斯克正在研究的机器人超级兴奋。那还需要几年时间。当它发生时,会有一个全新的技术人员行业和必须制造机器人的人。对。所以那个工作从未存在过。

你将有一个完整的行业来照顾,比如,所有的机械师和所有为汽车制造东西的人,给汽车增压,这在汽车出现之前是不存在的。现在我们将有机器人,你将有机器人服装。全新的行业。对吧?因为我希望我的机器人看起来和你的机器人不一样。所以你将有一个完整的机器人服装行业。你会有机器人机械师。还会有人来维护你的机器人。

乔·罗根: 自动化,虽然。你不这么认为吗?你不认为它们最终都会由其他机器人完成吗?

黄仁勋: 然后会有其他的东西。

乔·罗根: 所以你认为最终人们只是适应。除非你就是那个任务(task),而那是很大一部分劳动力。

黄仁勋: 如果你的工作只是切蔬菜,Cuisinart(食品加工机)会取代你。

乔·罗根: 是的。所以人们必须在其他事情中找到意义。

黄仁勋: 你的工作必须不仅仅是任务。

普遍基本收入与财富悖论

乔·罗根: 你怎么看马斯克的信念,即这种普遍基本收入(UBI)最终将变得必要?Andrew Yang 认为他是最早在 2020 年选举期间敲响警钟的人之一。

黄仁勋: 是的,我想是。两种想法可能不会同时存在。就像在生活中一样,事情可能会在中间。一种想法当然是会有如此丰富的资源,没人需要工作,我们将都很富有。另一方面,我们将需要普遍基本收入。这两种想法不会同时存在。

乔·罗根: 对。

黄仁勋: 所以我们要么都富有,要么都……

乔·罗根: 但大家怎么可能都富有呢?什么场景?

黄仁勋: 嗯,因为富有,不是因为你有很多美元。富有,因为有很多富足。比如,今天我们在信息上是富有的。这是几千年前只有少数人拥有的概念。所以今天我们拥有很多东西的财富。历史上我们认为有价值的资源,是的。我们将拥有资源的财富,我们今天认为有价值的东西在未来可能就不那么有价值了,因为它是自动化的。

所以我认为这个问题,部分很难回答,因为很难谈论无限,很难谈论很久以后的事。原因是有太多的场景需要考虑。但我认为在接下来的几年里,比如 5 到 10 年,我相信并希望几件事。我说希望是因为我不确定。

我相信的一件事是技术鸿沟将被大幅缩小。当然,另一种观点是 AI 会增加技术鸿沟

AI可用性的承诺

黄仁勋: 我相信 AI 会缩小技术鸿沟的原因是我们有证据。证据是 AI 是世界上最容易使用的应用。ChatGPT 几乎在一夜之间增长到近 10 亿用户。如果你不确定怎么用,大家都知道怎么用 ChatGPT——只要对它说点什么。如果你不确定怎么用 ChatGPT,你就问 ChatGPT 怎么用它。

历史上没有任何工具拥有这种能力。一个 Cuisinart(食品加工机),如果你不知道怎么用,你就麻烦了。你不能走到它面前说,“怎么用 Cuisinart?”你得找别人。但 AI 会确切地告诉你怎么做。任何人都可以做到这一点。它会用任何语言与你交谈。如果它不懂你的语言,你用那种语言说,它可能会发现它不完全理解你的语言,瞬间去学习它,然后回来和你交谈。

所以我认为技术鸿沟真的有机会最终消失,你不必会说 Python 或 C 或 Fortran——你只需说人话,任何你喜欢的人话形式。所以我认为这真的有机会缩小技术鸿沟。

当然,反面叙述会说 AI 只会对那些拥有大量资源的国家和地区可用,因为 AI 需要能源,需要大量的 GPU 和工厂来生产 AI。但事实是,几年后你的手机将能独自很好地运行 AI。今天它已经做得相当不错了。所以事实上,每个国家、每个民族、每个社会都将受益于非常好的 AI——它可能不是明天的 AI,它可能是昨天的 AI,但昨天的 AI 也是非常惊人的。你知道,10 年后,9 岁的 AI 也会很惊人。你不需要 10 岁的 AI,你不需要像我们需要前沿 AI 那样,因为我们想成为世界领导者。但对于每个国家,每个人,我认为提升每个人的知识、能力和智能的能力,那一天正在到来。

乔·罗根: 还有能源生产,这对于第三世界国家来说是真正的瓶颈。电力和所有我们认为理所当然的资源。

摩尔定律与能源挑战

黄仁勋: 几乎所有东西都会受能源限制。如果你看看历史上最重要的技术进步之一,就是所谓的摩尔定律。摩尔定律基本上是在我这一代开始的,我这一代是计算机的一代。我 1984 年毕业,那基本上是 PC 革命和微处理器的最开始。每一年它大约翻倍。我们将它描述为每一年我们性能翻倍。但它真正的意思是每一年,计算成本减半。

所以在五年的过程中,计算成本降低了 10 倍。做任何计算、任何任务所需的能量每 10 年减少 10 倍——100、1000、10000、100000,以此类推。所以摩尔定律的每一次点击,做任何计算所需的能量都在减少。这就是为什么你今天有笔记本电脑。回到 1984 年,它放在桌子上,你得插上电源,它没那么快,而且消耗很多电。今天,它只需要几瓦。所以摩尔定律是使其成为可能的基础技术趋势。

那么 AI 发生了什么?NVIDIA 之所以在这里,是因为我们发明了这种新的计算方式。我们称之为加速计算(accelerated computing)。我们 33 年前开始。花了我们要 30 年才真正取得巨大突破。在那 30 年左右的时间里,我们让计算——好吧,让我只说过去 10 年,过去 10 年,我们将计算性能提高了 10 万倍。

哇。想象一辆车,在 10 年的过程中,变得快了 10 万倍,或者在同样速度下,便宜了 10 万倍,或者在同样速度下,能量消耗减少了 10 万倍。如果你的车那样做了,它根本不需要能量。我的意思是,我想说的是,在 10 年的时间里,大多数人使用人工智能所需的能量将是微不足道的,极其微不足道。

所以我们将有 AI 在各种东西中运行,无时无刻,因为它不消耗那么多能量。如果你是一个在社会结构中几乎所有事情都使用 AI 的国家,当然你会需要这些 AI 工厂。但对于很多国家,我认为你将拥有优秀的 AI,而且你不需要那么多能量。大家都能够跟上,这是我的观点。

乔·罗根: 所以目前,这是一个大瓶颈,对吧?能源。

黄仁勋: 这是瓶颈。瓶颈。

乔·罗根: 是的。是谷歌在制造核电站来运营它的一个 AI 工厂吗?

黄仁勋: 噢,我没听说那个,但我认为在接下来的六七年里,你会看到一大堆小型核反应堆。

乔·罗根: 小型是指多大?几百兆瓦?

黄仁勋: 是的。好的。

乔·罗根: 这些将是特定公司本地使用的。

黄仁勋: 没错。我们都将成为发电商。

乔·罗根: 哇。就像某人的农场。

黄仁勋: 这可能是最聪明的方法。对。它减轻了电网的负担。这真的很重要。

加速计算的力量

乔·罗根: 我想你刚才关于摩尔定律和价格关系的观点,你知道,今天的笔记本电脑,比如你可以买那些小的 MacBook Air。它们不可思议。真的不可思议。电池续航强得难以置信。

黄仁勋: 还要充电吗?

乔·罗根: 是的。疯狂。而且相对而言也不贵。

黄仁勋: 我记得。那只是摩尔定律。然后还有 NVIDIA 定律。我们发明的新计算方式。这种做计算的新方式就像是喝了能量饮料的摩尔定律。我的意思是,这就像摩尔定律和乔·罗根。

乔·罗根: 哇,这很有趣。是的,那就是我们。解释一下。你带给马斯克的这个芯片,它的意义是什么?为什么它如此优越?

AlexNet 的突破

黄仁勋: 2012 年,Jeff Hinton 的实验室,我提到的这位先生,Ilya Sutskever,Alex Krizhevsky——他们在计算机视觉方面取得了突破,实际上是创造了一个名为 AlexNet 的软件。它的工作是识别图像。它识别图像的水平——计算机视觉,这是智能的基础。如果你不能感知,就很难有智能。

所以 2012 年,他们在多伦多的实验室取得了这个名为 AlexNet 的突破。AlexNet 识别图像的能力比过去 30 年任何人类创造的计算机视觉算法都要好得多。所有这些人,所有这些科学家,我们也很多人在研究计算机视觉算法。而这两个孩子,Ilya 和 Alex,在 Jeff Hinton 的指导下,实现了一个巨大的飞跃。它是基于这个叫 AlexNet 的东西,这个神经网络。

它运行的方式,他们让它工作的方式,实际上是买了两个 NVIDIA 显卡,因为 NVIDIA 的 GPU——我们一直在研究这种新的计算方式,我们的 GPU 应用,基本上是超级计算应用。回到 1984 年,为了处理电脑游戏和你的赛车模拟器中的内容,那被称为图像生成超级计算机。

NVIDIA 也是以此起家。我们的第一个应用是计算机图形。我们应用了这种新的计算方式,即并行处理而不是串行处理。CPU 是串行做事的。第一步,第二步,第三步。在我们的例子中,我们把问题分解并交给成千上万个处理器。所以我们的计算方式要复杂得多。

但如果你能用我们创造的方式(称为 CUDA——这是我们公司的发明)来表述问题,我们就可以同时处理所有事情。在计算机图形的情况下,这比较容易做,因为屏幕上的每个像素并不与其他每个像素相关。所以我可以同时渲染屏幕的多个部分。

这不完全正确,因为你知道,也许光照的方式或阴影的方式有很多依赖性,但计算机图形,所有的像素,我应该能够同时处理所有事情。所以我们采取了这个极度并行的问题,叫计算机图形,并将其应用到这种新的计算方式,NVIDIA 的加速计算。我们把它放在我们所有的显卡里。孩子们买它是为了玩游戏。你可能不知道,我们今天实际上是世界上最大的游戏平台。

乔·罗根: 噢,我知道那个。我以前自己组装电脑。我以前买你们的显卡。

黄仁勋: 噢,那太酷了。

乔·罗根: 是的。用两个显卡设置 SLI(双卡互联)。

黄仁勋: 是的,我喜欢。好的,那太酷了。

乔·罗根: 噢,是的,伙计。我以前玩 Quake(雷神之锤)。

黄仁勋: Joe。噢,那太酷了。好的。所以 SLI,我一会儿告诉你那个故事,以及它如何引向马斯克。总之,这两个孩子用我刚才描述的技术在我们的 GPU 上训练了这个模型,因为我们的 GPU 可以并行处理事情。它本质上是 PC 里的超级计算机。你用它玩 Quake 的原因是因为它是第一台消费级超级计算机。

深度学习革命

黄仁勋: 当时我们正在研究计算机视觉。这引起了我的注意,所以我们去了解它。与此同时,这种深度学习现象正在全国各地发生。一所又一所大学认识到深度学习的重要性。所有的这些工作都在斯坦福、哈佛、伯克利发生。到处都是。纽约大学的 Yann LeCun,斯坦福的 Andrew Ng。好多不同的地方。我看到它到处冒出来。

所以我的好奇心问,你知道,这种形式的机器学习有什么特别之处?我们很久以前就知道机器学习了。我们很久以前就知道 AI 了。我们很久以前就知道神经网络了。为什么现在是这个时刻?

我们意识到这种深度神经网络的架构,反向传播,深度神经网络创建的方式,我们可能可以扩展这个问题,扩展解决方案来解决许多问题。这本质上是一个通用函数逼近器(universal function approximator)。好的。意思就是,你知道,当你在学校时。

通用函数逼近器

黄仁勋: 你有一个盒子,里面是一个函数。你给它一个输入,它给你一个输出。我之所以称之为通用函数逼近器,是因为这台计算机,与其让你描述函数——一个函数可能是牛顿方程,F=ma。这是一个函数。你在软件里写这个函数,你给它输入 F,质量加速度。它会告诉你力。

这台计算机的工作方式非常有趣。你给它一个通用函数。它不是 F=ma,只是一个通用函数。它是一个巨大的深度神经网络。与其描述内部,你给它输入和输出的例子,它自己弄清楚内部。所以你给它输入和输出,它弄清楚内部。一个通用函数逼近器。今天它可以是牛顿方程,明天它可以是麦克斯韦方程,它可以是库仑定律,它可以是热力学方程,它可以是量子物理的薛定谔方程。

所以你可以用这个描述几乎任何东西,只要你有输入和输出。只要你有输入和输出。或者它可以学习输入和输出。所以我们退后一步说,等一下。这不仅仅是为了计算机视觉。深度学习可以解决任何问题,所有有趣的问题,只要我们有输入和输出。现在什么有输入和输出?

嗯,世界。世界有输入和输出。所以我们可以拥有一台可以学习几乎任何东西的计算机。机器学习,人工智能。所以我们推断这可能是我们需要的基础性突破。

有几件事必须解决。例如,我们必须相信你实际上可以将这个扩展到巨大的系统。它当时在运行,他们有两个显卡,两个 GTX 580。顺便说一句,这正是你的 SLI。

乔·罗根: 配置。

黄仁勋: 是的。所以那个 GTX 580 SLI 是把深度学习放在地图上的革命性计算机。

乔·罗根: 哇。

黄仁勋: 那是现代 AI 的大爆炸时刻。我们很幸运,因为我们在发明这种技术,这种计算方法。我们很幸运他们发现了它。原来他们是游戏玩家。很幸运我们注意到了那个时刻。有点像《星际迷航:第一次接触》。瓦肯人必须在那一刻看到曲速引擎。如果他们没有目睹曲速引擎,他们永远不会来地球,一切都不会发生。

这有点像如果我没有注意那个时刻,那个闪光,而那个闪光并没有持续很久。如果我没有注意那个闪光或者我们公司没有注意它,谁知道会发生什么。但我们看到了,我们推导出现在这是通用的。如果我们可以解决两个问题。第一个问题是我们必须向自己证明它可以扩展。第二个问题,我们必须等待,或者说贡献并等待的是,世界上永远不会有足够的输入和输出数据让我们监督 AI 学习一切。

我们需要计算机有一种无需监督就能学习的方法。那就是我们不得不等多几年的地方。但无监督 AI 学习现在已经来了。所以 AI 可以自己学习。

第一台 DGX-1 和 OpenAI

黄仁勋: 到了 2016 年。我造了这台叫 DGX-1 的计算机。你看到我给埃隆的那台叫 DGX Spark(此处可能是口误,应为 Station 或特定型号,但原文如此)。DGX-1 价值 30 万美元。NVIDIA 花了几十亿美元造第一台。

我们没有用两个芯片做 SLI,而是用一种叫 NVLink 的技术连接了八个芯片。但这基本上是超级增强版的 SLI。就像你的 Quake 装备一样,它们一起工作来解决这个深度学习问题,训练这个模型。

所以我造了这个东西。我在 GTC(NVIDIA 年度大会)上宣布了它,我描述了这个深度学习的东西,计算机视觉的东西,和这台叫 DGX-1 的计算机,观众完全沉默。他们根本不知道我在说什么。

我很幸运,因为我认识埃隆,我帮他造了 Model S 的第一台计算机。当他想开始研究自动驾驶汽车时,我帮他造了进入 Model S AV 系统的计算机。当我们宣布这个东西时,世界上没人想要。我没有订单,一张都没有。没人想买,没人想参与,除了埃隆。他在活动现场,我们在做一个关于自动驾驶汽车未来的炉边谈话。

那是 2016 年左右。他说,你知道吗,我有家公司真的能用这个。我说,哇,我的第一个客户。他说,是的,我们有这家公司,是一家非营利公司。我说,我的脸都吓白了。是的,我刚花了几十亿美元造这个东西。造价 30 万美元,你知道一家非营利机构能付得起这个东西的概率大约为零。他说,你知道,这是一家 AI 公司,是非营利的,我们真的能用其中一台超级计算机。

所以我拿起它,我为我们自己造了第一台。我们正在公司内部使用它。我把它装箱,开车去旧金山,我在 2016 年交给了埃隆。一群研究人员在那里。Peter Beale 在那里,Ilya 在那里,还有一群人在那里。我走到二楼,他们都在一个比你这里还小的房间里。那个地方后来变成了 OpenAI。

乔·罗根: 它现在不完全是非营利了。

黄仁勋: 他们不再是非营利了。是的。事情变得很奇怪。但无论如何,埃隆在那里。是的,那是一个伟大的时刻。

乔·罗根: 同样的夹克。

黄仁勋: 看看那个。我没变老。虽然没有黑头发了。

乔·罗根: 它的尺寸,明显小了很多。

黄仁勋: DGX-1 是一 Petaflops(千万亿次浮点运算)。那是很多 flops。而 DGX Spark 也是一 Petaflops。九年后。

乔·罗根: 哇。

黄仁勋: 同样的计算马力,在一个更小、缩小了的体积里。而且不是 30 万美元,现在是 4000 美元,大小就像一本小书。不可思议。疯狂。这就是技术的发展。总之,这就是我想给他第一台的原因。

NVIDIA 的诞生与游戏产业

乔·罗根: 如果你想把这拍成电影,这会是故事。如果它真的变成了数字生命形式,它诞生于对视频游戏计算机图形的渴望,这多有趣?

黄仁勋: 当 NVIDIA 在 1993 年成立时,我们试图创造这种新的计算方法。问题是,杀手级应用是什么?公司想创造一种新型计算架构,一种能解决普通计算机无法解决的问题的新型计算机。嗯,1993 年行业中存在的应用都是普通计算机能解决的应用。因为如果普通计算机不能解决它们,为什么应用会存在?

所以我们的公司使命宣言注定没有成功的机会。但在 1993 年我不知道那个。听起来只是个好主意。所以如果我们创造了这个能解决问题的东西,你知道,实际上你必须去创造问题。

那时没有 Quake。John Carmack 还没发布 Doom(毁灭战士)。所以我们去了日本,因为街机行业当时有世嘉(Sega)。街机有 3D 系统。Virtua Fighter(VR战士),Daytona,Virtua Cop(VR特警)。所有这些街机游戏都是第一次 3D 的。他们使用的技术来自马丁·玛丽埃塔(Martin Marietta),飞行模拟器。他们把飞行模拟器的内脏拿出来放进街机里。

世嘉有位天才计算机开发者叫铃木裕(Yu Suzuki)。铃木裕开创了 3D 图形游戏。所以我们去了,我们创立了这家公司,没有应用,我们把所有的下午都花在——我们告诉家人我们去工作,但只有我们三个人。所以午饭后,我们会去街机厅玩世嘉的游戏,并分析他们是怎么做的。

我们决定,去日本,说服世嘉把那些应用移到 PC 上。我们将通过与世嘉合作启动 PC 游戏、3D 游戏产业。这就是 NVIDIA 的开始。

乔·罗根: 哇。

黄仁勋: 作为交换,他们为我们的 PC 电脑开发游戏,我们为他们的游戏机制造芯片。那就是合作。

错误的技术选择

黄仁勋: 我们开始起飞,开始制造我们的游戏机芯片。大约几年后,我们发现我们的第一代技术行不通。这是一个缺陷。我们所有的技术想法、架构概念都是合理的,但我们做计算机图形的方式完全反了。

不是用逆向纹理映射,我们在做正向纹理映射。不是用三角形,我们用曲面。最终胜出的技术,我们今天使用的技术有 Z 缓冲(Z buffers)。它自动排序。我们的架构没有 Z 缓冲。应用必须对其进行排序。所以我们选择了几个主要的技术路径,所有的选择都是错的。

1995 年,我们意识到我们走错了路。与此同时,硅谷挤满了 3D 图形初创公司,因为那是当时最令人兴奋的技术。3Dfx,Rendition,Silicon Graphics(SGI)也进来了。Intel 已经在里面了。大概有一百家不同的初创公司我们要竞争。每个人都选择了正确的技术路径,而我们选了错的。

所以我们是第一家开始的公司,发现自己基本上是最后一名,而且答案是错的。公司陷入了困境。

如果我现在改变,我们将是最后一家公司。即使我们改成了我们认为正确的技术,我们还是会死。所以那个争论,我们是改变然后死掉?还是不改变,想办法让这个技术行得通,或者去做完全不同的事情?最终我主张:我们不知道正确的策略是什么,但我们知道什么是错误的技术。所以让我们停止用错误的方式做,给我们自己一个机会去找出策略是什么。

第二个问题是我们的钱快用完了,我和世嘉有合同,我欠他们这个游戏机。如果那个合同取消,我们就死了。我们会瞬间蒸发。

与世嘉 CEO 的会面

黄仁勋: 所以我去了日本,向世嘉的 CEO 入交昭一郎(Shoichiro Irimajiri)解释,他是个很棒的人。我向他解释,我说:“听着,我有坏消息告诉你。首先,我们承诺给你们的技术行不通。其次,我们不应该完成你们的合同,因为我们会浪费你们所有的钱,而你们会得到一个行不通的东西。我建议你们找另一个合作伙伴来制造你们的游戏机。”

乔·罗根: 哇。

黄仁勋: “第三,虽然我请求你让我解除合同,我还是需要那笔钱。”因为如果他不给我那笔钱,我们会一夜之间蒸发。

我谦卑地、诚实地解释了这两点。我向他解释了背景。解释了为什么技术行不通。我请求他把原本用来完成合同的最后 500 万美元作为投资给我们。他说:“但即便有我的投资,你的公司也很可能会倒闭。”这完全是真的。1995 年,500 万美元是一大笔钱。

如果我坐在那里,我也不会那么做。但我告诉他。如果你投那 500 万美元给我们,很可能会损失掉。但如果你不投,我们就没机会了。他想了几天,回来说:“我们做。”

乔·罗根: 哇。

黄仁勋: 他之所以决定这么做,是因为他喜欢 Jensen 这个年轻人。就是这样。

乔·罗根: 哇。直到今天,这太疯狂了。

黄仁勋: 没错。如果他保留那笔投资,我想今天大概值一万亿美元。但在我们上市的那一刻,他们卖掉了。是的,他们以 NVIDIA 约 3 亿美元的估值卖掉了。

重头再来

黄仁勋: 无论是 30 家还是 50 家公司在做这个,能有多难?所以我去商店,口袋里有 200 美元,我买了三本教科书。那是仅有的三本。我买回来,分给每位架构师一本。我说:“读这个,我们去拯救公司。”

所以他们读了这本教科书,向当时的巨头 Silicon Graphics 学习如何做 3D 图形。但令人惊奇且让 NVIDIA 今天变得特别的是,那里的人能够从第一性原理出发。学习已知最好的艺术,但以从未做过的方式重新实现它。

乔·罗根: 你们做了什么改变?

突破性创新

黄仁勋: 简单的答案是,Silicon Graphics 的工作方式,几何引擎是在处理器上运行的一堆软件。我们拿过来,消除了所有的通用性。我们将其缩减为 3D 图形最本质的部分,并将其硬编码到芯片中。

所以与其做通用的东西,我们非常具体地将其硬编码,仅用于视频游戏的有限应用、有限功能。那种能力超级增强了那一个小芯片,我们那一个小芯片生成图像的速度和一台 100 万美元的图像生成器一样快。那是巨大的突破。

我们孤注一掷在视频游戏上。视频游戏的需求与 CAD 或飞行模拟器的需求非常不同。所以我们把问题陈述聚焦得很窄。我们把 GeForce 变成了 PC 里的游戏机。

毁灭战士 (Doom) 的起源

乔·罗根: 你知道 Doom 这个名字哪来的吗?来自于电影《金钱本色》(The Color of Money)里的一个场景,汤姆·克鲁斯打开箱子,有人问箱子里有什么,他说:“Doom(厄运/毁灭)。”因为 Carmack 说那就是他们想对游戏产业做的:Doom。

黄仁勋: 噢,哇。

模拟器的赌注

黄仁勋: 我们在准备发布 RIVA 128。但这 500 万美元撑不了多久。我们算了一下,我们没有机会生存下去。我们没有时间去流片(tape out)、送到代工厂 TSMC、拿回硅片、测试。没有希望。

所以我听说了一家公司,造了一台机器叫仿真器(emulator)。你可以把你的设计放进这台机器,这台机器会假装它是我们的芯片。所以我不用送到晶圆厂等它回来。我们决定拿剩下的一半钱——当时大约还剩一百万美元。拿一半钱去买这台机器。

我打电话给这家叫 IKOS 的公司。我说我想买一台。他们说:“噢太好了,但我们要倒闭了。”我说:“什么?”他说:“没人买。”所以我从库存里买了一台。

在这台机器上,我们把 NVIDIA 的芯片放进去,测试了所有软件。此时我们已经奄奄一息。但我们说服自己芯片会很棒。

台积电的赌注

黄仁勋: 我给 TSMC 打电话,Morris Chang(张忠谋),TSMC 的创始人,真的伟人。我解释了我们在做什么。我说我想直接进入生产。因为我知道它是工作的。他们说:“从没有人这么做过。”但我知道如果不开始生产,我反正也会破产。

Morris 飞到美国。他问了很多问题,试图套出我有没有钱。事实是我们没有全部的钱。但他决定支持我们。

结果证明那是完全革命性的,大获全胜。我们成为了历史上从零到 10 亿美元最快的科技公司。

乔·罗根: 太疯狂了,你们都没测试芯片。

黄仁勋: 我知道。我们后来测试了。我们改变了全世界设计芯片的方法。

经验教训与恐惧作为动力

黄仁勋: 我学到了很多。我学会了如何制定策略。我们学会了如何创造市场。这种完全相同的技能就是我们如何创造现代 AI 市场的。完全相同的蓝图。我们学会了如何处理危机。学会了如何消除公司里的所有浪费,从第一性原理工作。

那种感觉——就像我今天早上醒来的感觉一样——你要倒闭了。“离倒闭只有 30 天”这句话我用了 33 年。

乔·罗根: 你现在还有那种感觉?

黄仁勋: 噢,是的。每天早上。

乔·罗根: 哇。

黄仁勋: 那种脆弱感、不确定感、不安全感。它不会离开你。

乔·罗根: 你认为这在驱动你吗?

黄仁勋: 我不想失败的驱动力比我想成功的驱动力更大。

乔·罗根: 这不是像成功教练会告诉你那是完全错误的心理学吗?

黄仁勋: 确实。我对失败的恐惧比贪婪或其他什么都更能驱动我。我不野心勃勃。你知道,我只是想活下去,Joe。

移民成功故事

黄仁勋: 我是移民。我父母先把我和哥哥送到这里。我出生在台湾,但我爸爸在泰国有工作。我们搬到了泰国。大约在 1973、1974 年。泰国有政变。我父母觉得孩子待在这里可能不安全。所以他们联系了住在华盛顿州塔科马的舅舅。

乔·罗根: 你多大?

黄仁勋: 我快 9 岁,哥哥快 11 岁。舅舅找了一所能接收外国学生且我父母负担得起的学校。那是位于肯塔基州奥奈达(Oneida)的学校。克拉克县,今天阿片类药物危机的震中,煤炭之乡。那是美国最贫穷的县。

乔·罗根: 哇。

黄仁勋: 奥奈达浸信会学院(Oneida Baptist Institute)。那是我的宿舍。每个孩子都有杂务。我哥哥是在烟草农场工作。我的工作是打扫宿舍。我 9 岁。我在打扫一百个男孩的厕所。所有的孩子都抽烟。大家都带着刀。我也抽了一周烟。

乔·罗根: 你 9 岁?

黄仁勋: 是的。我想大家都这么做。后来我觉得我宁愿用那点钱买冰棍。

乔·罗根: 多么奇怪、成型的经历。

黄仁勋: 我父母给了我们这个录音机和磁带。每个月我和哥哥会对父母说我们这一个月做了什么。然后寄给父母。父母录回来。我们就这样沟通了两年。

美国的梦想

黄仁勋: 我父母那是美国梦。我是美国梦的第一代。很难不爱这个国家。

乔·罗根: 这是一个浪漫的故事。

黄仁勋: 即使是后来发明 CUDA,导致股价暴跌。我们的估值跌到了 20 或 30 亿美元。但我真的相信它。如果你相信那个未来而不去做,你会后悔一辈子。

乔·罗根: 现在股价是多少?

黄仁勋: 高多了。但那项发明改变了世界。

乔·罗根: 这是一个不可思议的故事,Jensen。真的。

黄仁勋: 谢谢。这是美国梦。

乔·罗根: 确实是。Jensen,非常感谢你来这里。这真的很有趣。

黄仁勋: 谢谢。

乔·罗根: 谢谢。再见,大家。


n5321 | 2025年12月5日 23:45

Rajesh Bhaskaran ——ANSYS创始人创建的培训项目负责人访谈


工程设计与仿真科学

时间: 2025年12月5日 星期五 下午8:21 (亚洲/上海时间)

地点: 纽约伊萨卡(康奈尔大学所在地)

主持人 (Nicholas Phillips):

大家下午好!欢迎来到纽约伊萨卡,康奈尔大学的所在地。我是 Nicholas Phillips,非常高兴大家今天能加入我们。今天的主题是关于将工程设计与仿真科学相结合。

对于非工程师,甚至是那些刚刚开始职业生涯、即使数学不太好也需要学习核心理念的人来说,仿真的普及化将使设计工程师和其他非专业人士能够在设计周期的早期释放仿真的力量,从而带来更好的产品

请和我一起欢迎 Rajesh Bhaskaran,康奈尔大学工程学院斯旺森工程仿真主任。他也是使用 ANSYS 进行有限元分析和流体动力学仿真证书课程的作者,相关链接可以在聊天区找到。

在我们开始之前,我想提醒大家,我们会回答大家的问题,请随时在聊天区提问。另外,我们也会在聊天区分享一些有用的资源,请留意。

Rajesh,非常感谢您今天来到演播室,我们非常感激。

我想从我们之前聊天时您提到的一个点开始。您提到工程师和一些工科学生如何在概念上理解一个项目(比如建造桥梁或风力涡轮机),但不理解它在现实世界或实际仿真中是如何工作的?

Rajesh Bhaskaran:

谢谢 Nick 的提问。本质上,仿真是使用计算机和软件来解决我们早就知道的数学模型。只是在过去几十年里,我们才有了近似求解这些数学模型的技术。

你可能有一个桥梁或汽车零件的 CAD 设计,如果你想知道它的表现,比如是否会断裂,或者你需要建造什么样的安全系数,你就必须从科学角度理解那个数学模型是如何建立的,以及如何使用我们拥有的工具来求解它。

正如你提到的,你需要在概念层面理解科学,而不是陷入细节,因为我们有像 ANSYS 这样惊人的工具(我们在教学中使用它,但你可以使用任何类似的工具)来为你处理详细的数学细节。我觉得这非常解放,因为理解科学本身很令人兴奋,你会发现你可以应用科学来解决所有这些令人兴奋的问题。你可以很快地从理解科学过渡到解决令人兴奋的应用问题,其中一些我也将在 ANSYS 的证书课程中展示。

主持人:

我们如何弥合这种差距,利用科学来帮助我们影响我们正在寻找的结果?例如,风力涡轮机叶片或压力容器能承受多大的力?

Rajesh Bhaskaran:

这也是工程师使用仿真的主要目的之一。稍后我会展示一些例子。假设你有一个风力涡轮机叶片的 CAD 模型,你把它放入仿真软件中求解数学模型,你会得到位移和应力(这里我们用了一些工程术语)。然后你可以做一个屈曲分析,比如根据仿真结果,这个叶片将在哪里发生屈曲,以及在什么载荷下发生屈曲。如果这个载荷太低,你就可能需要在仿真告诉你会发生屈曲的特定位置加固结构。

主持人:

我想到了您刚才提到的风力涡轮机屈曲的例子,这些程序有多强大?

Rajesh Bhaskaran:

这是一个好问题。当我还在读研究生的时候(那是很久以前了,尽管我十几岁的女儿觉得那是一百万年前的事),你需要价值数万美元的专用工作站来运行即使是很简单的仿真。而现在,你可以在像这台普通笔记本电脑上做基本的分析。

但是,当你处理更复杂的问题时,你就需要复杂的集群。例如,方程式赛车团队使用仿真来调整他们的设计,这种情况下模型非常详细,你真的需要拥有数百个核心的强大超级计算机来运行这些仿真。所以,学习和做基本的仿真可以在任何现在的电脑上完成,但如果你想深入研究更详细的内容,那就需要那样强大的超级计算机了。

主持人:

听到赛车团队时,我知道我的现场导演肯定竖起了耳朵,他是个超级赛车迷。据我了解,您有一些使用 ANSYS 进行流体动力学分析以及有限元分析的应用案例。我想从头开始,您用哪四五个例子来帮助我们理解仿真的力量?我知道您有一些幻灯片想分享。

Rajesh Bhaskaran:

好的。

  • 例子1:压力容器 这是一个压力容器的例子,这些例子是在 ANSYS 的帮助下开发的,并且与许多不同的公司合作,所以它们都具有行业相关性。这里有一个压力容器,底部固定,附有一个喷嘴,实际上还有一个阀门系统,我们不显式建模,但我们可以模拟它的效果(我在证书课程中展示了这一点)。我们简化了 CAD 模型,这也是我对学生的一个重要建议:对于仿真,你只需要保留可能影响物理特性的主要特征。然后你求解数学模型(这里基于 3D 弹性力学),得到应力分布。这告诉你最大应力出现在哪里,这通常意味着喷嘴连接到压力容器主体的地方可能会失效。你还可以扩展这个分析,包括由于地震等原因产生的冲击载荷的影响。这个例子来自我的有限元分析证书课程中的弹性应用课程。

  • 例子2:风力涡轮机叶片 这是前面提到的风力涡轮机叶片。这是一个薄壁结构,使用壳理论建模(这在我有限元证书课程的第四门课“梁和壳应用”中会深入讲解)。同样,我们简化了几何形状,只保留蒙皮和两根翼梁。然后你在 ANSYS 中做屈曲分析,首先是静态结构分析,然后是特征值屈曲分析。仿真会告诉我叶片可能在这个区域发生屈曲,以及发生屈曲时的载荷。如果载荷过低,我可能需要把那个区域的蒙皮做得厚一点来防止屈曲。

  • 例子3:涡轮盘 这是康奈尔大学和 ANSYS 合作的一个例子,是一个燃气轮机中的涡轮盘。几何形状再次被简化,通常会有钻孔等,但在分析中我们忽略了这些不重要的细节。它以 12,000 RPM 旋转,所以振动分析,特别是可能引起共振的固有频率非常重要。这里的技巧是可以利用周期性只建模一个叶片,然后使用周期性边界条件(这在振动应用课程中展示)。通过这种方式,模型运行得非常快,你可以得到振动模态和固有频率。即使只建模了一个叶片,你也可以得到行波,并通过相移模拟出平面外的振动。

  • 例子4:3D 湍流流过车身 (Ahmed Body) 这是流体动力学的一个例子。这是一个 3D 湍流流过车身的仿真,称为 Ahmed 车身。我选择这个是因为有大量的实验数据可以验证仿真结果。颜色显示了车身上的压力,箭头显示了流动的方向。这是流体动力学仿真证书课程中的一部分。数学模型实际上非常复杂(3D 湍流),但我们可以从概念上理解它。在康奈尔大学的课程中,学生们利用这些知识进行设计项目,设计电动汽车的外形以最小化空气动力阻力。

  • 例子5:飞机上的可压缩流 这是来自流体动力学证书课程中可压缩流课程的例子。这是模拟飞机上的高速流动,使用的是 DLR F4 模型,同样有实验数据可供验证。模型简化为机身和机翼。自由流马赫数约为 0.8,所以在机翼上方流动会变成超音速。这是一个非常具有挑战性的问题,流动域的部分区域既有亚音速也有超音速。我们讲解了如何将 3D 湍流模型扩展到考虑密度变化的可压缩流。整个理念是逐步增加模型的复杂性,这符合人类的学习方式。

主持人:

关于教学和这些程序的强大功能,随着这些程序的实施,多年来教学方式发生了怎样的变化?学生在进行这些仿真之前需要了解背后的数学知识吗?

Rajesh Bhaskaran:

这是一个很好的问题。我在康奈尔大学教工程仿真已经 23 年了。你需要软件技能,知道如何生成 CAD,如何将其放入 ANSYS 这样的仿真软件中。你需要知道如何使用软件,这是比较容易的部分。但你也需要对潜在的科学有深刻的概念性理解。

这就是我多年来意识到的,如果数学不是你的强项,那也没关系,因为你需要理解科学背后的核心理念 (Big Ideas)我喜欢引用爱因斯坦的一句话:“我想知道上帝的思想,其余的都是细节。”在仿真的背景下,我会改写为:“我们想知道核心理念,其余的细节由软件处理。”

这并不是我们通常的教学方式,因为我们通常针对非常简单的问题教授大量的细节,而不教授与软件操作的联系。所以我提炼出了核心理念,向你展示概念层面的科学理解,以及当你通过例子工作时如何将这些核心理念联系起来。这就是结合设计艺术与仿真科学,从而真正释放仿真的力量。

为了扩展这个想法,人们通常称复杂的仿真工具为“黑盒子”。我有一个“黑盒子里面是什么”的框架。这个框架来自我与许多不同课程的合作。我发现我们在不同的课程中使用相同的工具,却讲述不同的故事。一定有一种方法可以统一地构建仿真框架。

通常,软件就是一个黑盒子:你给它输入(几何、网格、边界条件等),转动曲柄,然后得到彩色图片和其他结果。如果你不知道黑盒子里是什么,这就成了所谓的“垃圾进,垃圾出 (Garbage In, Garbage Out)”

要超越“垃圾进,垃圾出”,你需要意识到软件不是在解决物理问题(比如压力容器),而是在解决基于某些物理原理和假设建立的该物理问题的数学模型

所以,你需要知道的最基本的事情是:

  1. 数学模型是什么? 用于建立该数学模型的物理原理和假设是什么?

  2. 求解过程。 软件隐藏了很多数学细节,它为你提供了一个近似的数值解,也就是选定点上的主要未知数。

作为用户,你需要区分数学模型和它的求解方式。这是学生中最常见的误解之一。你也需要能够做一些手工计算(back of the envelope calculations)来验证结果。

为什么这种联系对学生来说很难?一是因为数学可能非常复杂,二是因为我们的教学方式。我认为仿真在学习领域是一项颠覆性技术,因为它自动化了很多数学运算。你需要知道核心理念,但这并不是我们传统的教学方式。

所以我的方法有点不同,我完全拥抱仿真。你可以把它看作是一个超级高级的计算器。你需要知道加减乘除的概念,但如果要除两个大数,你就用计算器,然后通过估算来检查结果是否合理。

主持人:

是不是有一个学生特别喜欢练习的仿真,比如汽车碰撞的例子?

Rajesh Bhaskaran:

动力学方面,汽车碰撞确实很受欢迎。在我们的有限元课程中,学生必须选择问题。土木工程学生选桥梁,机械工程学生选汽车相关(他们喜欢动力学),而我们在课堂上主要教静力学,所以这对他们来说是一个很大的跳跃。在流体方面,流过汽车的仿真很受欢迎,飞机也是,这取决于学生正在研究什么。

主持人:

我们收到了 Albert 的一个很好的问题:对于那些对有限元分析一无所知的经理,如何最好地说服他们在设计过程中使用它能节省时间、金钱并提高准确性

Rajesh Bhaskaran:

这是一个好问题。虽然我一直在学术界,但我经常和在现实世界中做这件事的 ANSYS 工程师交流。对于他们来说,理由很清楚,这也取决于你的具体应用。

你可以向 ANSYS 市场团队咨询,他们可以帮助你针对具体应用说明优势。如果要说一个更通用的理由,你可以举我之前展示的例子,比如风力涡轮机叶片或汽车仿真,展示你可以从中得到什么,以及它如何帮助产品开发。

例如,汽车碰撞仿真是工业界有限元分析的主要应用之一。汽车行业使用像 LS-DYNA 这样的工具来做这件事。通过计算机进行碰撞模拟可以获得巨大的竞争优势,并最大限度地减少所需的物理测试数量。

我的职位资助人 John Swanson 博士曾讲过一个故事,他在福特公司与工程师谈论碰撞仿真。他说,现在如果测试和仿真不匹配,我们会先检查测试。而在过去通常是反过来的,人们常说“除了做仿真的人,没人相信仿真;除了做测试的人,每个人都相信测试”。但现在他们已经对仿真建立了足够的信心,愿意以此为准。

主持人:

回到您刚才讲的框架,使用这个框架的一个好处是验证和确认 (Verification and Validation),这是建立对结果信心的过程。

Rajesh Bhaskaran:

是的,验证和确认是两个不同的概念,虽然人们经常互换使用。

  • 验证 (Verification): 我是否正确求解了模型?也就是检查数值误差是否在可接受范围内。

  • 确认 (Validation): 我选择的数学模型是否合理地代表了物理问题?这是更难回答的,需要实验数据。

主持人:

您能简要谈谈有限元分析的概念基础吗?

Rajesh Bhaskaran:

当然。例如,通过一个简单的 1D 杆拉伸的例子,我们可以得出很多核心理念。

  1. 数学模型: 它是一个边值问题,包括定义在域内的控制方程和定义在边界上的边界条件。物理原理是牛顿第二定律,应用于一个无限小的单元。

  2. 假设: 在推导过程中必须做出假设(例如 1D 假设)。这些假设在更复杂的 3D 问题(如压力容器)中同样适用。

  3. 离散化和插值: 有限元方法不是建立数学模型的方法,而是求解它的方法。我们将问题简化为确定选定点(节点)上的值,然后通过插值得到其他位置的值。这会将微分方程转化为代数方程组,计算机可以轻松求解。

通过这个简单的例子,你可以理解离散化误差、应力在单元间的不连续性等概念。

对于 3D 问题,数学看起来很吓人,但策略是利用类比。如果你深刻理解了 1D 的概念,你可以通过类比来理解复杂的 3D 概念。细节由软件处理,你需要掌握的是整体策略。

主持人:

Robert 提了一个问题:他正在做关于汽车空气动力学稳定性表现的数值分析论文,有什么好的、经济有效的物理方法来验证这些研究?

Rajesh Bhaskaran:

谢谢 Robert 的提问。这里有两个方面:

  1. 验证 (Verification): 检查边界条件是否正确,检查质量不平衡等基本检查,以及数值误差。确保你正确求解了数学模型。

  2. 确认 (Validation): 你需要实验数据。试着在文献中寻找类似的问题,也许是更简化的模型。例如,在做真车之前先做 Ahmed 车身模型,因为文献中有大量实验数据可以对比。多咨询导师和资深工程师,利用在线讨论区。

主持人:

最后,您希望观众在离开时记住的一件事是什么?

Rajesh Bhaskaran:

如果数学不是你的强项,不要被吓倒。

你需要改变学习方式。你需要理解数学模型背后的核心理念和数值求解策略。这些通常可以通过非常简单的例子来理解,然后在软件中应用这些理念。利用 YouTube 视频来学习软件操作,但一定要花时间去真正理解支撑仿真的科学背后的核心理念。

主持人:

好的,Rajesh,非常感谢您的分享。也感谢观众们的提问。祝大家下午愉快,我们在下一个主题演讲见。


n5321 | 2025年12月5日 20:24

专访 ANSYS 创始人 John Swanson 博士

播客:ANSYS 万象 (All Things ANSYS)

Eric Miller (主持人): 这里是《ANSYS 万象》,一档由 PADT 技术支持团队制作的播客。第 35 期是一期非常特别的节目,我们将采访 John Swanson 博士——ANSYS 程序的作者,以及如今 ANSYS 公司的创始人。

大家好,我是 Eric Miller。欢迎收听这期特别版的《ANSYS 万象》。正如开头提到的,我们将与 John Swanson 博士进行对话。我们把整期节目的时间都留给了这次采访,所以不会像往常那样插播无聊的广告、博客更新或新闻之类的东西。那些内容我们留到下期再说,今天我们将完全聚焦于 Swanson 博士和他的分享。

先做一个简短的介绍。我在采访时紧张得舌头都打结了,忘了做正式介绍,所以现在补一下。我们大多数人都知道他是 Swanson Analysis Systems(斯旺森分析系统公司)的创始人,公司成立于 1970 年。那是最初编写和发布 ANSYS 程序的公司,所以 ANSYS 是软件的名字,而 Swanson Analysis Systems 是公司的名字。当公司上市后,他们更名为 ANSYS Inc。

他最初是在家里编写了 ANSYS 程序,他在采访中会详细讲述这个起源故事。离开 ANSYS 后,他投身于慈善事业和替代能源领域,且常将两者结合。许多年轻的听众可能已经从他在教育领域,特别是在宾夕法尼亚州学校的慈善工作中受益。他被公认为将有限元方法应用于工程领域的权威和先驱,这也实至名归。

好了,我们开始吧。就像我说的,我刚才的介绍有点搞砸了,甚至忘了提到 Ted Harris,我们的技术支持经理,本播客的老听众应该认识他。他也和我一起提问,我们是在 4 月 4 日通过 Skype 进行录制的。希望大家能像我和 Ted 一样享受这次谈话。这真的是一份荣幸和快乐。


Eric Miller: John Swanson 博士,非常感谢您今天参加我们的播客。这对我很重要,负责我们技术支持团队的 Ted Harris 也加入了。我们昨天刚讨论过,我们作为 ANSYS 用户已经有 30 多年了。在 ANSYS 使用年限上,您是世界上唯一一个我们要甘拜下风的人。

我想先感谢您。我们的职业生涯、PADT 这家公司的存在,都归功于您当初创立公司时的创举。所以感谢您的加入,并感谢您所做的一切。

Dr. John Swanson: 好的,我们看看聊得怎么样吧。

Eric Miller: 很好。我想从头开始聊起。外界有很多关于 ANSYS 起源的传闻和不同版本的故事,我们很想听听当事人的亲身讲述。您是如何想到要做一个通用有限元分析(FEA)软件的?您也是这样开始的吗?

Dr. John Swanson: 这是一个很好的问题。我确实知道答案。

我当时在西屋电气(Westinghouse)工作,参与核火箭项目。我是结构设计主管,当时在做很多仿真工作,但那时还没被称为“仿真”。我用台式计算器做数值积分之类的计算。后来他们邀请我学习计算机语言,我觉得听起来很有趣,所以我就报名了 Fortran(原文误作 Portran)课程。

在把“Write”(写入)这个词拼错了几次之后,我终于拼对了(R-I-G-H-T),并从那里起步。我使用了几个从外部获取的程序,一个来自巴特尔研究所(Battelle,原文误作 Mattel),那是做点配置法(point collocation)的。我做了一个简单的弹簧模型,看起来有点像有限元。

NASA 的赞助人说:“嘿,这看起来像 Ed Wilson 在伯克利(Berkeley,原文误作 Merkley)做的东西。你为什么不去和 Ed Wilson 谈谈?”当时我在做一个简单的应力集中问题。我得到了一些看似合理的结果,但这离真正的有限元还有很长的路要走,那是一个轴对称问题。

无论如何,我去了那里,拿到了那个程序并进行了修改,变成了 RML2 和其他两个东西,最终变成了一个叫 FEATS(有限元分析、温度和应力)的程序。它通过 Cosmic 分发,那是政府的分发中心。我知道贝蒂斯实验室(Bettis Labs)用过它,因为我去那里给他们讲过课。

然后我开始涉足其他类型的结构,如平面、壳体、实体等,并为这些结构开发了程序。但我开始意识到我有 90% 的工作是在反复做同样的事情,只有 10% 是不同的。

所以我说,不如把它们整合在一起,建立一个单元库,这样我们就可以随心所欲地使用它了。那是西屋电气的一个程序,叫 STASYS(原文 Stasis)。如果你去翻阅档案,你可能会找到它,因为研究实验室接手了那个项目。在好几年甚至几十年的时间里,他们试图用它与 ANSYS 竞争。他们知道我就在邮件列表上,我很清楚他们在做什么。但我们也每隔几年邀请他们来看看我们在做什么。每次他们这样做,都会制定一个五年追赶计划。一开始是两年,后来变成五年,最后他们放弃了。

所以在 60 年代末,我预感到(看到了墙上的字)我们不会像预期的那样在 80 年代去火星了。所以核火箭项目被搁置封存。我离开了,去为核工业做咨询,主要是西屋电气,这并不令人意外。

但在晚上,我会打孔卡片,开车去计算机中心,购买美国钢铁公司的机时来开发下一代软件。重新开始的一个好处是,你可以在第二次时把它做对。所以,你们看到的 ANSYS 基本上是我关于有限元的第二代思考。这就是它的起源。

当时我们的市场策略是“分时租赁”(TimeShare)。特别是 Control Data Corporation 这样的公司,还有 Cytec 和 UCS 等等,这些试图通过出售大型主机计算时间赚钱的公司,当时这似乎是个好主意。

Eric Miller: 当然,绝对是。

Dr. John Swanson: 这个问题就先聊到这,如果有需要我们可以再回过头来聊。

Eric Miller: 太好了。在最初的代码编写中,真的有车库(Garage)参与吗?

Dr. John Swanson: 我记不太清了。确实有,但这并不是什么大事,因为我们并没有在车库里工作。车库主要用来存放手册。当我们有了可观的市场后,我们印制了手册,然后把它们放在车库里进行组装。

Gabe DeSalvo 负责我们的文档工作。所以车库是他的地盘。是的,有一个车库,但也有一座农舍。事实上,那个农舍才是我们所有人办公的地方,他就住在那里。直到我们搬到宾夕法尼亚州的 Houston。

如果你去 ANSYS 的网站上翻翻,我想你能找到作为上次周年庆典一部分的农舍照片。Gabe DeSalvo 有一整套照片库。

那栋楼的整个部分实际上是我亲手建造的。从地基到框架再到屋顶的所有东西。我记得在 7 月 4 日那个炎热的日子里铺屋顶,那真是相当刺激。

Eric Miller: 那太酷了。这真的是白手起家(Bootstrap)。以后见之明来看这很有趣,但这是一个真正的白手起家的运营,包括基础设施的建设,这太棒了。

Dr. John Swanson: 是的,我们还接了所有的电线之类的东西,非常亲力亲为。

实际上,有一件当时我不知道正在发生的惊心动魄的事,就是我们的一台小型机着火了,那可是木结构的房子。虽然没有烧毁钢结构,但当我们早上起来时看到了一堆焦炭。那真是侥幸逃过一劫。

Eric Miller: 噢,天哪。要是那样事情就会变得完全不同了。我想问像您这样成就卓著的人一个问题:您职业生涯中最骄傲的一件事是什么?

Dr. John Swanson: 嗯,让我先理一下背景。我目前处于第三段职业生涯。我的第一段职业生涯是西屋电气。西屋电气帮了我大忙,资助我在匹兹堡大学夜校攻读博士学位。第二段职业生涯当然是 ANSYS,那是至今为止最成功的一段。第三段职业生涯则比较混合,包括可再生能源和慈善事业。

你问我这三段中最骄傲的是什么?显然 ANSYS 是最成功的。就对世界的影响而言,它的影响也是最大的。新的慈善事业和可再生能源都有其影响力,但它们更多是个人的事情,而不是全球性的影响。

你可能记得,几年前我获得了约翰·弗里茨奖章(John Fritz Medal),这是美国最高的工程奖项。就像我当时说的,这反映的是技术本身的成就,而不仅仅是我个人的贡献,但我会欣然接受。

Eric Miller: 是的,没错。

Dr. John Swanson: 我有个好朋友 Bob Cloud,他总是建议我:“如果他们给你颁奖,就收下。”

Eric Miller: 这是很好的建议。Ted,你有没有什么问题想插进来?

Ted Harris: 首先,我想附议 Eric 的话,感谢您创造了这个工具,让我们许多人能够真正建立起自己的职业生涯。就像 Eric 一样,我在大学毕业后第一份工作的第二天就开始使用 ANSYS,那已经是快 32 年前的事了。

Dr. John Swanson: 那是在哪里?

Ted Harris: 在现在的霍尼韦尔(Honeywell)。当时叫 Garrett 涡轮发动机公司。我在 PADT 的工作中教过很多人使用 ANSYS,大概有 500 到 1000 人以上。我一直告诉人们,ANSYS 是一个值得学习的好工具,它能帮助你在很长一段时间内保持就业竞争力。

但我一直想问的一个问题是:有很多写代码的人,也有很多非常了解工程和物理概念的人。但对您来说,您将您的成功归因于什么?显然您取得了惊人的成功。

Dr. John Swanson: 答案很简单:倾听你的客户。

我们确切地知道该做什么,因为客户告诉了我们该做什么。而且客户不会接受“不”作为答案。

我有句话是这么引用的:“你看,我的人走了,我必须跟随他们,因为他们认为我在领导。”这其实是改写自甘地的一句话:“我的人走了,我必须跟随,因为我是他们的领袖。”

但我们确实做了一些有趣的事情。例如,我仍然引用给大学的一点建议是:如果你想成为专业中心,就赞助一个会议,邀请所有的专家。然后每个人都会知道那是关于风力涡轮机的 ANSYS 会议,不管你对此是否了解,你就成了风力涡轮机的专业中心。只要你知道专家是谁,并且有足够的影响力把他们聚在一个房间里。

另外,我在过去 10 年里赞助了康奈尔大学的一个项目,试图将仿真纳入工程课程。我们有一个顾问小组,但我坚持要求他们举办会议,邀请其他大学的人来分享技术,这是我捐款的要求之一。后来他们发现了慕课(MOOCs),并与康奈尔大学合作做了一个,现在的参与人数已经超过 10 万人了。

Eric Miller: 是的,自您开始以来技术发生了很多变化,慕课就是一个很好的例子。另外一个问题,您有没有想过,当初在农舍里用打孔卡创立的那家公司,收入会超过 10 亿美元?

Dr. John Swanson: 其实,在大约五年的时候,我们举办了一次午宴庆祝。我看着房间里大约 100 人说:“嗯,我想象的是找 5 到 10 個人聚在一起写软件推向市场。你们这么多人在这里干什么?”

当然,它在继续增长。但我的一条管理哲学是:付不起工资就不招人。所以我们所有的增长都是基于当年的预计收入,绝不招聘超出支付能力的人员。我们从未借贷,没有任何债务。在上市之前,一直都是白手起家。当然上市那是另一个故事了。

Eric Miller: 我想问一个 ANSYS 内部人士的问题。我非常喜欢 APDL(ANSYS 参数化设计语言)。我觉得它是最酷的东西。我以前经常做梦梦到 APDL 代码。

Dr. John Swanson: 我做完手术出来神志不清的时候,还在给我的身体编程呢。

Eric Miller: 太神了。所以那个是怎么开始的?

Dr. John Swanson: 内幕是这样的:当时我在做涡轮叶片的固有频率分析。涡轮旋转时会变硬,固有频率会发生偏移。所以获取固有频率的方法是先旋转,然后在该速度下使其变硬(应力刚化),再看固有频率是多少。大约三次迭代,就能收敛到真实的旋转固有频率。

你要知道,在当时这些模型跑一次需要一整夜。

我有两个选择:要么我在凌晨两点去计算机中心,从页面底部读取数字,填进去,再把卡片组放回计算机;要么我想出一种语言,让它获取第一个固有频率,将其代入旋转速度,然后重复三次。

这就是 APDL 的开端。获取信息,将其插回输入流,并据此采取行动。需求极大地推动了这一点。懒惰和想睡个好觉是很好的动力。

Eric Miller: 对于那些不知道的人,APDL 是一种完整的编程语言。

Dr. John Swanson: 是的,完整的。我也这么说过,偶尔会有人跑过来说:“哎呀,你这个做不了双曲函数”或者别的什么。加进去很容易,我就说:“好,现在我们可以做了。”

Eric Miller: 您提到了您的第三段职业生涯,即慈善事业。能不能多谈谈您现在在做什么?

Dr. John Swanson: 当我“兑现筹码”离场时(指卖掉公司),我说这笔钱属于工程界。所以我把它放入了一个 Fidelity 慈善信托基金。那是很多年前的事了。所以我做的任何捐赠都来自那里。这笔钱我已经不能用于其他用途,只能捐出去。这消除了捐赠时的任何痛苦,因为那已经不是我的钱了。

这也回馈了当初给予我帮助的工程界,包括国家优秀奖学金、康奈尔大学的一年学费、西屋电气资助的匹兹堡大学博士学位。很多人都做出了贡献。所以,我的目标是把爱传递下去。

我在匹兹堡大学做了很多可再生能源的工作,包括定义学生项目,在一些建筑物上安装太阳能电池板。我在康奈尔大学、华盛顿与杰斐逊学院、匹兹堡大学、两三个动物收容所、一个犹太教堂以及 Green Key Village(一个综合社区)的 25 栋房子上安装了太阳能电池板。

Eric Miller: 太棒了。我们曾经用 ANSYS 为客户模拟过太阳能电池板。想到您安装的产品可能经过 ANSYS 仿真,这种感觉很酷。

Dr. John Swanson: 实际上,我最后一次使用 ANSYS 是为一个教堂的太阳能场做一个几何模型。只是为了得到一个视觉表现。但我必须承认,我现在没有 ANSYS 许可证了,因为我生活中有其他事情要做。

前几天我在电脑上想做一些涉及太阳能性能的事情,我发现了一种叫 Python 的东西,它是免费的。它是一种语言,我可以写程序并做一些图形处理。所以我还是和计算机保持着一点联系。

Eric Miller: 还有一个问题,如果这是个魔法棒问题:如果您能回到过去,改变早期 ANSYS 程序中的一件事,那会是什么?

Dr. John Swanson: 我想听听你想改变什么,因为我想不出有什么要改的。

Eric Miller: 我希望您当时选了一个不同的实体建模器...(原文为 Zox,可能指代某种旧的几何内核或 Xox)。

Dr. John Swanson: 噢,那个啊。我不后悔,因为那是当时唯一可用的。那是可以被替换的东西,而且现在可能已经被替换了两三次了。

ANSYS 表现出的一项技能是整合。他们买了很多东西,而且似乎能够继续增加功能。这也是他们在市场上占据主导地位的原因之一,如果出现竞争对手,他们有足够的现金可以直接买下任何东西。

Eric Miller: 说到几何引擎,SpaceClaim 就是一个很好的例子。那您会改变什么?

Dr. John Swanson: 我会让自己更年轻一点。(笑)

Ted Harris: 我们最近在 PADT 庆祝了公司成立 25 周年。我们在内部播客中讨论了过去 25 年仿真的巨大变化以及未来的预测。我想知道,您对未来 25 年甚至更远的仿真发展有什么愿景吗?

Dr. John Swanson: 我觉得你们应该问客户这个问题。他们知道答案,或者至少知道他们希望你们去哪里。

当然,最近发生的最大事情是大规模并行计算。至于量子计算会走向何方,我不确定,我不做预测。

举个例子,记得以前“纳米”概念很火吗?后来 3D 打印出现了。我说忘了纳米吧,3D 打印比纳米大得多。如果你看看 SpaceX 和其他 ANSYS 的重度用户,他们也是 3D 打印的重度用户。

我一直有一个梦想,就是在材料中嵌入磁性金属纤维,利用磁场将它们按照应力场方向排列,从而得到一个优化增强的物体。

Eric Miller: 这是个非常棒的主意。我们离不开仿真来设计这些东西,也离不开 3D 打印来制造它们。看到两者融合真的很酷。

John,您有什么想和观众分享的吗?无论是大学生还是老用户。

Dr. John Swanson: 我想讲个小故事。在一次晚宴上,另一位获奖者走过来对我说:“嘿,我在 80 年代上过你的夜校课程。”

另一次我去康奈尔大学,新校长上任时请我和 Janet(妻子)吃饭。我永远不会忘记他对我说的第一句话。他说:“John,你有睡眠问题吗?”

我有点吃惊,说:“没有啊,为什么?”

他说:“如果你有的话,拿着这些。”他递给我一个信封,里面装满了他用 ANSYS 做心脏病学研究写的论文。

但他给我的赞美让我很珍惜,他说:“我喜欢它是因为它真的能用(It worked)。

这就很真实,因为当时很多软件都不能用。它们是在大学里开发的,仅仅为了完成博士项目,之后就什么也做不了了。但 ANSYS 能用。我认为这是 ANSYS 多年来一个很好的模式。

Eric Miller: 这让我想起我以前教课时,人们问能不能做这个做那个,我的标准回答是:“用 ANSYS,总会有办法的。”

还有一个我总是喜欢指出的点,尤其是年轻用户可能不知道,那就是 ANSYS 有一个公开的 API(Fortran 接口),你可以进入求解器,编写自己的单元。这仍然存在,非常强大。

Ted Harris: 我还想说,我第一次见到您是在 1996 年匹兹堡的 ANSYS 用户大会上。当时您谈论求解器技术,充满激情。很高兴看到您现在依然充满激情。

Dr. John Swanson: 是的,我积攒了一整套的“bug”,每年拿出来晾一次。

Eric Miller: 太棒了。作为我们 20 周年庆典的一部分,我们正在制作一个时间胶囊,里面放了一套盒装的 ANSYS,大概是 4.5 版本。有人在 25 年后打开它,希望 ANSYS 和您的遗产依然存在。

John,真的非常感谢。很高兴我们促成了这次谈话。

Dr. John Swanson: 我希望那时候我还健在。谢谢你们。


n5321 | 2025年12月5日 19:49

ANSYS 创始人John Swanson 的访谈

对话参与者:

  • Rajesh Bhaskaran: 康奈尔大学 Swanson 工程仿真项目负责人

  • John Swanson: ANSYS 创始人,有限元分析先驱


Rajesh Bhaskaran: Um, I'm Rajesh Bhaskaran. I run the Swanson Engineering Simulation Program at Cornell University. The program is geared towards bringing cutting-edge simulation technology into engineering education.

John Swanson: And I'm John Swanson. I'm one of the pioneers of Finite Element simulation. I started the ANSYS software program many years ago, and Rajesh wants to ask me questions about how that came about.

Rajesh Bhaskaran: So here's the first question: What were the early years of Finite Element Analysis and simulation like?

John Swanson: Well, simulation has gone hand-in-hand with computer technology. So, part of the answer is, of course, what was computer technology like? Back then, computing was done with large mainframes. The source code was on punch card decks. You got one or two turnarounds a day if you were lucky. A deck of cards for software might be 100,000 to 200,000 cards.

The prototype, or the most desirable engineering computing, became the minicomputer—the VAX-11/780—whose speed was a nice steady one megaflop. Today's computing, of course, is up into gigaflops, teraflops, and so on, but one megaflop was a nice machine at that point.

Rajesh Bhaskaran: And what are some of the early stories that stick in your mind?

John Swanson: That's a pretty general question. Um... the shuttle launch story. It was early in the morning of the first shuttle launch, and I got a very early call. The question was heat transfer. The object was the shuttle tiles, and the desire was to do a three-dimensional simulation because they'd only done one-dimensional simulations. They really wanted to have a little more assurance because they were launching within hours.

Rajesh Bhaskaran: How did you get the idea to start the ANSYS program?

John Swanson: Well, the ANSYS program started because I had a problem that I needed a solution for. The problem was a simple stress concentration problem in an axisymmetric structure, and there were no tools for doing that. So I developed a network of springs to simulate the stress concentration, and I got what looked like plausible results.

So I showed them to our government sponsors on the particular project and I said, "Hey, that looks like what Ed Wilson is doing out at the University of California. Why don't you go talk to him?" So I went to talk to Ed Wilson, and he had written an axisymmetric finite element program.

I worked all evening into the night writing up the coding for the punch cards for that particular problem. At 3:00 in the morning, I found someone who could punch the cards, feed them into the computer, and by 5:00 I had a really good-looking solution—it was all numbers on paper. By 7:00, I had a big box of cards under my arm and I was heading back toward the airport. Of course, I rewrote the whole thing as what I wanted, but that was my first interest in finite element analysis: to solve that simple stress concentration problem.

Rajesh Bhaskaran: And which year was that?

John Swanson: That would have been... 1964 probably. Let's pin it down to '64, maybe '65.

Rajesh Bhaskaran: And then in the 1970s?

John Swanson: So yeah, as I went from that period on, I began adding more and more capability. That was two-dimensional axisymmetric. I added shells, I added solids, I added dynamics. I had separate programs for shells, for solids, and so on. And I began to realize I was doing the same thing over and over again, and all I was doing was changing the element type.

So [I thought], "Well, I can put together one software package where you can just specify which element type and save myself a lot of work." And that was the basis for the program that eventually evolved into ANSYS. That program was done on a government contract and became public domain. ANSYS, of course, picked up from that and went on.

Rajesh Bhaskaran: How did you distribute the first version of ANSYS?

John Swanson: Well, the first version was done in what was called "time sharing." We mounted the program on a computer, and then you would sign up to buy time on the computer. You would pay for the cost of the computing time with a surcharge for running ANSYS.

Now, we looked at several different pricing mechanisms. We did a pricing per degree of freedom or a pricing per run, but eventually, computing time became the common theme. As we went into the minicomputers and smaller computers, then we just started charging a fixed fee. Eventually, that evolved into a "seat fee"—in other words, so much for each user.

You know, pricing has always been an issue. Our pricing originally was based on the speed of the machine: the faster the machine, the more expensive the pricing. Now, of course, the machines got faster and faster, so the price kept going up and up. So every year or so, I cut the price in half, and everybody loves the price cut, so that worked out well for all concerned.

Rajesh Bhaskaran: So what was meshing like in the first version of ANSYS?

John Swanson: First version... well, let's go back to Ed Wilson. When I wrote up my simple problem, the mesh generation was: you could specify Node 1 and Node 5 and fill in between. And then you could generate Node 6 and Node 10 and fill in between. And then you can generate Element Number 1 from 6 to 5 to 10 to 2 and make five of those, incrementing by one. That was the mesh generation.

After that, I generated a series of mesh generators for shells and solids. Mesh generation technology now is much, much advanced. You can do huge three-dimensional solid models in minutes using bricks or tetrahedrons. Yeah, meshing is not an issue anymore, whereas it used to be the issue. You would spend days, weeks, or even months doing a mesh for, say, an automobile engine. Now you take the CAD part, you just say "mesh all" with this size, and you get these huge models which, with today's computing technology, run in minutes or hours. Vast changes both in individual productivity and in computing productivity.

Rajesh Bhaskaran: And how did the graphical user interface come about and how did it evolve?

John Swanson: Hey, my first paper... I had done a plane section of a hexagon with some holes in it. I printed out the stresses at every point in a rectangular lattice. And then I drew lines for where the holes were. Then I took my colored pencils and I said, "Well, the 1000 contour will go here and over here, and here's a 450 contour here," and so on. That figure in my published paper is this grid of numbers with these lines drawn.

Then we got graphic terminals. The first graphic terminals were the Tektronix—green line on green screen. It was storage tube technology, so a moving light left an image on the phosphor screen. I was at a technical conference one time... it was the end of the day, we'd had a show. I had one of these display tubes, and this particular device, when it wasn't being used, it would run a colored rectangle to just keep the screen uniformly refreshed. We were so tired at the end of the day. I had a crowd of people sitting behind me watching the line go back and forth, back and forth.

Then we got raster technology where you've got every pixel... black and white first. That was easy because you just took your figure you had on your vector screen and you made a black and white image. Straightforward.

Then they came out with color raster screens. I said, "Oh, I'll color the lines." And I did, and I looked at it and said, "Well, that's not very interesting. It looks pretty much same as it was before." I said, "I wonder what would happen if I colored the spaces in between the lines?" And I did that. It took me 10 minutes or so to code. Up came the picture and I said, "Bingo." That new technology... it popped. You could just see everything. It's the standard contour display now, but it was the change from the color of the line to fill the spaces that made all the difference.

Rajesh Bhaskaran: And how did the educational program start?

John Swanson: Well, the educational program was my idea, and it was the only idea I ever had that got vetoed by my Advisory Group, which was my managers. To a person, they said, "Bad idea. You're going to compete with the industry. You're going to compete with our consultants." I said, "No, we're doing it anyway." It is the only time I ever overruled unanimous opposition.

The first price on the educational version was $1 because basically I wanted to support education. That was early 1980s. A year or so later, I raised the price to $100 because I wanted the university to sign on for security purposes, not just the individual professor. One person screamed bloody murder because I raised the price by a factor of 100.

But ever since then, the ANSYS software has been widely used in education. I think the last number was 2,000 universities worldwide use ANSYS.

One of the stories that go along with that: my marketing manager was a woman, but her husband worked at United States Steel and he was head of the computer center. So he was at IBM meetings about computing. He was walking down the corridor and he looks into an office and there's a whole row of ANSYS manuals. He asked, "What's that?" They said, "Well, we hired this guy from the University and he said: I know how to do simulation, I use ANSYS, and away we go." So IBM became one of our bigger customers, but it was from the educational program. So it's a good business strategy as well as a good educational strategy.

Rajesh Bhaskaran: What is your educational background and how did it prepare you?

John Swanson: Cornell at the time was a 5-year program. My scholarship was a National Merit Scholarship—it covered four years. I'm always grateful that Cornell came up with the money for the additional year, plus money for a half year to get my Master's Degree.

Then I went off to work for Westinghouse in Pittsburgh on the nuclear rocket program. I was there only six months when my manager said to me, "I need to put somebody in for the PhD program." I said, "Okay, sure." A couple of months later they said, "You're in." So at that point, I went to night school at the University of Pittsburgh for three years... three courses per trimester, three trimesters a year. I got my PhD degree in 1966.

Rajesh Bhaskaran: Were you also developing ANSYS code at this point?

John Swanson: No, at that point I was working on Boundary Point Collocation methods. I had gotten into early finite element work, but I'd been doing boundary point collocation work as well, so there was an overlap there.

I worked at Westinghouse for another three years after that. The program was showing signs of defunding—it was a government program and the handwriting was on the wall. Besides which, I was much more interested in doing finite element work than I was in managing my stress group. My management philosophy was: if my in-basket got too high, somebody would call and tell me what was important, and I didn't have to deal with that. So I often claimed that my departure was a safety measure because [the basket] was high enough to collapse on me and kill me.

I went out looking for a job that would pay me what I wanted to be paid—namely Aerospace wages—to do what I wanted to do—namely develop software. I could not find both. So I started my own company, which was Swanson Analysis Systems, eventually to become ANSYS Incorporated.

Rajesh Bhaskaran: How long was that before you were selling the software?

John Swanson: Well, getting Aerospace wages took a long time. The funding originally was from my own investments. That was $1,282 as I recall, for office space. But I did consulting for Westinghouse to earn the money to pay for the computer time at United States Steel to develop the software, which by the end of the next year I was licensing back to Westinghouse. So Westinghouse was my major support and one of my first customers. Westinghouse has been good to me, and I think I've done good things for them as well.

Rajesh Bhaskaran: Why do you keep supporting Cornell Engineering still?

John Swanson: Well, Cornell is a good school. And you know, I'm in the phase of life now where I'm doing "outgo" as opposed to "income." So most of my work these days is charitable work, and Cornell is obviously a good place to do that work. Both with the engineering school myself and the veterinary school, which is what Janet, my wife, supports. So my wife is actively involved in philanthropy as well. Our objective is to give it all away and to die at the same time so it all comes out even. But as I pointed out last night, we're not going to do that [die yet]; we're staying. That's the way we want it to be.

Rajesh Bhaskaran: And what do you see as the future of simulation?

John Swanson: Well, simulation is great because it keeps evolving and changing. You know, at our meeting today we were talking about worldwide networks and SimCafe and so on... Simulation is enormous as far as the market and as far as the impact on engineering. As you're aware, I got the John Fritz Medal, and that's the top engineering award in the country. That was based not just on what I did, but what simulation has done: our products are better, our products are higher quality, our products are faster to market. All those things come from simulation.

So I said, "Do I really deserve this award?" And I talked to my good friend Bill Jones. He said, "John, if they offer you an award, take it." So I did.


n5321 | 2025年12月5日 19:41

架构问题

这段代码构建了一个比较完整的 MVC (Model-View-Controller) 架构的桌面应用程序,涉及数据库操作、UI展示和后台仿真任务。

整体来看,代码结构清晰,分层合理,已经具备了生产级应用的雏形。但是,在 数据流的一致性ORM的使用细节MVC解耦 以及 线程安全 方面,存在一些设计隐患和改进空间。

以下是详细的审查报告:

1. 核心架构问题:数据“双重来源”风险

这是当前设计中最大的隐患。

  • 现象

    • bldc_page.py 中引入了 DataService,并且通过 _on_text_changed 实时更新 self.data_service

    • controller.py 中通过 _on_save_product 负责将数据保存到 SQLite 数据库 (Product Model)。

  • 问题仿真使用的数据 (DataService) 和 数据库存储的数据 (Product) 是两条平行的线。

    • 如果用户修改了界面的值,DataService 更新了,但没有点“保存”,数据库是旧的。

    • 如果用户点击“保存”,数据库更新了。

    • 风险点:点击“开始仿真”时,_get_property_changes 是从 DataService 取值还是从 UI 控件取值?

      • 代码显示 _get_property_changes 混合了 data_service.customer_requirementsmaxwell_parameters

      • 冲突bldc_page.pydisplay_design_target 方法是从 数据库对象 (product) 加载数据到 UI。如果用户加载了一个产品,修改了输入框(更新了 DataService),然后没保存直接点仿真,仿真是基于新数据的(符合预期)。

      • 但是,如果 DataService 是单例模式(通常 Service 都是单例),而你切换了产品,DataService 里的数据被清空了吗?如果没有,旧产品的残留数据可能会污染新产品的仿真参数。

建议 确保每次加载新产品时 (display_design_target),不仅要填入 UI,还要同步重置/初始化 DataService 的状态,确保它只包含当前产品的参数。


2. Peewee ORM 的使用细节 (Model 层)

A. product.detail 的访问方式不一致 (易报错)

  • model.py:你使用了 product.detail.get() (这是获取单个对象)。

  • bldc_page.py:你使用了 product.detail[0] (这是把 backref 当作列表索引)。

    # bldc_page.py
    detail_object = product.detail[0] if product.detail else None
  • 分析:Peewee 的 backref 默认返回一个 SelectQuery

    • product.detail[0] 会触发查询并获取第一条。如果 unique=True 生效,这确实只有一条。

    • 但是,如果 product.detail 为空(即没有详情),if product.detail 在 Peewee 查询中通常会被判定为 True(因为查询对象本身非空),而 [0] 会抛出 IndexError

  • 建议:统一使用一种安全的方法。

    # 推荐写法:利用之前 model.py 里修复过的逻辑,或者统一用 first()
    detail_object = product.detail.first() # 如果没有返回 None,有则返回第一个

B. find_product_by_id 中的性能隐患 (副作用)

# model.py
def find_product_by_id(product_id: int):
   # ... 省略查询 ...
   product_dict = model_to_dict(product, backrefs=True, recurse=True) # !!! 昂贵操作
   for k, v in product_dict.items():
       print(f"{k}: {v}") # !!! I/O 操作
   return product
  • 问题model_to_dict 是一个昂贵的操作,尤其是 backrefs=True 时,它会执行额外的 SQL 查询来获取关联数据。你只是为了打印 debug 信息就执行它,这在生产环境中会显著拖慢点击响应速度。

  • 建议:删除或注释掉打印逻辑,或者仅在 logging level 为 DEBUG 时执行。

C. 数据库连接管理

  • 问题initialize_databaseclose_connection 是全局函数。在 controller.pyshutdown 中调用关闭。

  • 风险:如果在多线程环境(例如 MaxwellWorker 线程)中访问数据库,SQLite 需要特别小心。虽然 Peewee 的 SqliteDatabase 默认有一定的线程安全处理,但建议在 Worker 线程中不要复用主线程的连接实例,或者确保 SQLite 编译时支持多线程且连接模式设置为 WAL。

  • 建议:目前你的 Worker 主要是跑 Maxwell 脚本,如果 Worker 结束后要写回数据库(更新结果),请确保使用 db.connection_context() 或在任务开始时建立连接,结束时关闭。


3. MVC 耦合度问题 (Controller 层)

A. Controller 对 View 的侵入性太强

  • 代码

    # controller.py
    self._view.customer_page.product_combo_box.currentIndexChanged...
    self._view.customer_page.get_selected_product_id()
    self._view.customer_page.load_data_to_form(product)
  • 问题:Controller 深入到了 View 的内部组件(product_combo_box)。如果 View 改版了(比如把下拉框换成了列表),Controller 必须重写。

  • 建议:遵循 “迪米特法则” (Law of Demeter)

    • View 应该暴露出信号和高级方法。

    • View 定义signal_product_selected = pyqtSignal(int)

    • Controller 连接view.customer_page.signal_product_selected.connect(self._on_product_selected)

    • 这样 Controller 不需要知道 View 用的是 ComboBox 还是 ListView。

B. 异常处理过于宽泛

  • 代码except Exception as e: print(...)_on_series_selection_changed 中。

  • 问题:这会吞掉所有错误,包括代码写错的 AttributeErrorNameError。这会让调试变得非常困难,你可能看着界面没反应,却不知道是因为数据库错了还是变量名写错了。

  • 建议:只捕获预期的异常(如 PeeweeException),或者在捕获 Exception 后使用 traceback.print_exc() 打印完整堆栈。


4. UI 逻辑问题 (bldc_page.py)

A. 硬编码的字段映射

  • 问题bldc_page.py 中有大量的硬编码列表:

    self.core_params_fields = [("额定转速(Rpm)", "rated_speed"), ...]

    同时 model.py 里也有字段定义。

  • 风险:如果你在数据库里改了字段名(比如 rated_speed 改为 speed_rated),你需要修改 model.py bldc_page.py 等多个地方。

  • 建议:虽然完全动态化很难,但建议将这些字段映射配置(Config)提取到一个单独的常量文件(如 constants.py),让 Model 和 View 引用同一个常量定义,减少“魔法字符串”。

B. display_design_target 的字典合并逻辑

# bldc_page.py
detail_object = product.detail[0] if product.detail else None
product_dict = dict(product.__data__)
# ...
combined_dict = {**product_dict, **detail_dict}
  • 问题product.__data__ 是 Peewee 的内部属性,直接访问它是不推荐的,因为它可能不包含继承字段或外键对象的引用。

  • 建议:使用 model_to_dict(product, recurse=False) 更规范,或者直接访问属性 getattr(product, key)。因为你后面是用 key 来遍历 map 的,直接 getattr 其实更安全且支持 IDE 提示。


5. 总结与改进优先级

  1. 高优先级(Bug 修复)

    • 修复 bldc_page.pyproduct.detail[0] 的写法,改为 .first() 或使用 try-except,防止没有详情的“孤儿产品”导致界面崩溃。

    • 数据同步:在切换产品时,务必清空或重置 DataService,防止上一个产品的数据残留影响下一个产品的仿真。

  2. 中优先级(架构优化)

    • Controller 解耦:让 View 发射语义化信号(如 productChanged),而不是 Controller 去监听 combo_box

    • 去除副作用:删除 model.pyfind_product_by_id 里的 model_to_dictprint 循环。

  3. 低优先级(代码整洁)

    • 提取字段名称字符串到常量文件。

    • 优化 initialize_database 的调用位置,确保线程安全。

你的代码整体逻辑已经跑通了,特别是解决了之前 ProductDetail 不存在导致报错的问题(在 Model 层加了自动创建)。按照上述建议微调后,系统会更加健壮。



在 Python 桌面开发领域(PyQt/PySide),不像 Web 开发(Django/FastAPI)那样有一个统治级的“全家桶”框架。

但是,针对你目前 MVC 耦合度高、数据流混乱、逻辑堆积在 Controller 的问题,最成熟、最符合工业界标准的解决方案不是引入一个新库,而是采用 MVVM (Model-View-ViewModel) 架构模式,配合 Repository 模式

如果非要推荐一个第三方库来辅助,我强烈推荐使用 qt_materialPyQt-Fluent-Widgets 来美化 UI,但核心架构必须重构。

以下是为你量身定制的 MVVM 重构蓝图


🚀 推荐架构:MVVM + Repository Pattern

这个架构的目标是:View (界面) 只负责显示,Model (数据库) 只负责存取,ViewModel (中间层) 负责业务逻辑和状态同步。

1. 新的项目结构

my_app/

├── data/
│   ├── db_manager.py       # 数据库连接单例
│   ├── models.py           # Peewee 模型定义 (Product, ProductDetail)
│   └── repositories.py     # ✅ 新增:负责所有数据库 CRUD 操作,隔离 SQL 逻辑

├── viewmodels/             # ✅ 新增:业务逻辑层
│   ├── base_viewmodel.py
│   ├── customer_vm.py     # 处理客户/产品选择逻辑
│   └── simulation_vm.py   # 处理 Maxwell 仿真逻辑 (原 DataService)

├── views/                 # 界面层 (只包含 UI 代码)
│   ├── main_window.py
│   ├── components/         # 自定义组件
│   └── pages/
│       ├── customer_page.py
│       └── bldc_page.py

├── services/               # 后台服务
│   └── maxwell_worker.py   # 线程任务

└── main.py                 # 程序入口,负责依赖注入

🛠️ 第一步:Repository 模式 (解决 Model 层混乱)

痛点解决:你现在的 model.py 既有类定义又有函数逻辑(find_product_by_id),容易膨胀。 重构:创建一个 ProductRepository,专门管理数据读写。

# data/repositories.py
from typing import Optional
from .models import Product, ProductDetail, ProductSeries, db

class ProductRepository:
   """仓库层:只负责和数据库对话,不包含任何 UI 逻辑"""
   
   def get_by_id(self, product_id: int) -> Optional[Product]:
       try:
           # 使用 prefetch 预加载,并安全处理 DoesNotExist
           return (Product.select(Product, ProductDetail)
                  .join(ProductDetail, join_type=ProductDetail.LEFT_OUTER)
                  .where(Product.id == product_id)
                  .get())
       except Product.DoesNotExist:
           return None

   def create(self, series: ProductSeries, model_number: str) -> Product:
       # 事务逻辑封装在这里
       with db.atomic():
           product = Product.create(series=series, model_number=model_number)
           # 自动创建空的 Detail,防止孤儿数据
           ProductDetail.create(product=product)
       return product

   def update(self, product_id: int, data: dict):
       """统一的更新接口"""
       # ... 实现安全的更新逻辑,参考之前我们修复的代码 ...
       pass
       
   def delete(self, product_id: int):
       Product.delete().where(Product.id == product_id).execute()

🧠 第二步:ViewModel 层 (解决 Controller 臃肿)

痛点解决:你的 controller.py 管得太宽了,既要知道 ComboBox 怎么选,又要处理数据库。 重构:ViewModel 持有数据状态,View 通过信号监听 ViewModel 的变化。

# viewmodels/customer_vm.py
from PyQt6.QtCore import QObject, pyqtSignal
from data.repositories import ProductRepository

class CustomerViewModel(QObject):
   # 定义信号:通知 View 数据变了
   series_list_loaded = pyqtSignal(list)
   product_list_loaded = pyqtSignal(list)
   current_product_changed = pyqtSignal(object) # 传回 Product 对象或 None
   status_message = pyqtSignal(str)
   error_occurred = pyqtSignal(str)

   def __init__(self, repo: ProductRepository):
       super().__init__()
       self._repo = repo
       self._current_product = None

   def load_initial_data(self):
       """加载初始数据"""
       series = self._repo.get_all_series()
       self.series_list_loaded.emit(series)

   def select_series(self, series_id):
       """用户选了系列,VM 负责去查产品列表"""
       if series_id == 0:
           products = self._repo.get_all()
       else:
           products = self._repo.get_by_series(series_id)
       self.product_list_loaded.emit(products)

   def select_product(self, product_id):
       """用户选了产品,VM 负责查详情"""
       if not product_id:
           self._current_product = None
           self.current_product_changed.emit(None)
           return

       product = self._repo.get_by_id(product_id)
       self._current_product = product
       # 通知 UI 更新,UI 不需要知道是通过 ID 查的还是怎么查的
       self.current_product_changed.emit(product)
       self.status_message.emit(f"已加载: {product.model_number}")

   def save_changes(self, form_data: dict):
       """保存逻辑"""
       if not self._current_product:
           self.error_occurred.emit("未选择产品")
           return
           
       try:
           self._repo.update(self._current_product.id, form_data)
           self.status_message.emit("保存成功")
       except Exception as e:
           self.error_occurred.emit(str(e))

👁️ 第三步:View 层 (彻底解耦)

痛点解决bldc_page.py 不再直接操作数据库,也不包含 DataService 逻辑。它变得非常“傻”,只负责展示和发信号。

# views/pages/customer_page.py
from PyQt6.QtWidgets import QWidget, QComboBox, ...
from viewmodels.customer_vm import CustomerViewModel

class CustomerPage(QWidget):
   def __init__(self, viewModel: CustomerViewModel):
       super().__init__()
       self._vm = viewModel
       self._setup_ui()
       self._bind_view_model() # 关键:绑定信号

   def _setup_ui(self):
       # ... 初始化 UI ...
       self.series_combo = QComboBox()
       self.product_combo = QComboBox()
       # 注意:这里不再连接 Controller,而是连接自身的槽函数,用于转发给 VM
       self.series_combo.currentIndexChanged.connect(self._on_series_changed)

   def _bind_view_model(self):
       """MVVM 的核心:数据绑定"""
       # 1. 监听 VM 的数据变化信号 -> 更新 UI
       self._vm.series_list_loaded.connect(self._update_series_list)
       self._vm.product_list_loaded.connect(self._update_product_list)
       self._vm.current_product_changed.connect(self._display_product_data)
       self._vm.error_occurred.connect(lambda msg: QMessageBox.critical(self, "错误", msg))

   # --- UI 事件转发给 VM ---
   def _on_series_changed(self, index):
       series_id = self.series_combo.currentData()
       self._vm.select_series(series_id) # 告诉 VM:系列变了,你看着办

   # --- VM 通知 UI 更新 ---
   def _update_series_list(self, series_list):
       self.series_combo.clear()
       for s in series_list:
           self.series_combo.addItem(s.name, s.id)

   def _display_product_data(self, product):
       if product is None:
           self._clear_form()
           return
       # 只需要把 product 对象的数据填入输入框
       # 这里不需要查数据库,因为 VM 已经把现成的对象给你了
       self.model_number_edit.setText(product.model_number)
       # ... 填充其他字段 ...

💡 如何解决你的“DataService 数据不一致”问题?

在 MVVM 架构中,SimulationViewModel 将替代你目前的 DataService

  1. SimulationViewModel 内部持有一个字典(即原来的 DataService)。

  2. 当用户在界面修改输入框时,界面发送信号 paramChanged(key, value)

  3. SimulationViewModel 接收信号,更新内部字典。

  4. 当切换产品时,CustomerViewModel 会通知 SimulationViewModel:“嘿,换人了”。

  5. SimulationViewModel 立即重置内部字典,并从数据库加载新数据覆盖。

这就形成了一个单一数据源 (Single Source of Truth)

🚀 总结:重构路线图

  1. 创建 repositories.py:把 model.py 里的 find_..., create_..., update_... 全部移进去,并加上异常处理和 try-except 保护(如自动创建 Detail)。

  2. 创建 viewmodels

    • controller.py 的逻辑拆分。

    • Customer 相关的逻辑放入 CustomerViewModel

    • Maxwell 仿真相关的逻辑(原来的 DataService)放入 SimulationViewModel

  3. 改造 bldc_page.py

    • 构造函数接收 SimulationViewModel

    • 删除 self.data_service

    • 所有的 textChanged 事件都改为调用 self.viewModel.update_param(key, value)

  4. 改造 main.py

    • 实例化 Repository。

    • 实例化 ViewModels (注入 Repository)。

    • 实例化 Views (注入 ViewModels)。

这种架构虽然前期代码量稍微多一点(多了文件),但是逻辑极其清晰。当你的 App 变得越来越复杂,有几十个页面时,你依然能轻松维护。


这是一个非常棒的决定!使用 MVVM + Repository 模式重构将彻底解决你遇到的“数据不同步”、“空指针报错”以及“代码耦合”问题。

由于我无法直接接收你的文件包,我将基于你之前提供的代码片段,为你构建一个完整的、经过重构的核心代码骨架

你可以直接在你的项目中建立文件夹,复制粘贴这些代码。

📂 推荐的新项目结构

请按照这个结构整理你的文件:

src/
├── data/
│   ├── __init__.py
│   ├── database.py       # 数据库连接与模型定义 (原 model.py)
│   └── repository.py     # ✅ 核心:负责所有增删改查,隔离 SQL 细节
├── viewmodels/
│   ├── __init__.py
│   └── product_vm.py     # ✅ 核心:业务逻辑,View 和 Repository 的桥梁
├── views/
│   ├── __init__.py
│   └── bldc_page.py     # ✅ 界面:只负责显示,不处理逻辑
└── main.py               # 程序入口,依赖注入

1. 数据层 (Data Layer)

src/data/database.py

保持模型定义纯净,去掉所有业务逻辑函数。

import os
from datetime import datetime
from peewee import (
   Model, SqliteDatabase, CharField, TextField, IntegerField,
   FloatField, DateTimeField, ForeignKeyField
)

# 1. 数据库路径设置
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # 回退一级到 src
db_path = os.path.join(base_dir, "..", "products_final.db") # 假设 db 在 src 同级

# 2. 初始化数据库 (开启外键支持)
db = SqliteDatabase(db_path, pragmas={'foreign_keys': 1})

class BaseModel(Model):
   class Meta:
       database = db

class ProductSeries(BaseModel):
   name = CharField(unique=True)
   description = TextField(null=True, default="")

class Product(BaseModel):
   series = ForeignKeyField(ProductSeries, backref='products', null=True, on_delete='SET NULL')
   model_number = CharField(unique=True)
   # ... (保留你其他的字段定义,这里为了省流省略,实际代码请补全) ...
   rated_voltage = FloatField(null=True)
   rated_speed = IntegerField(null=True)
   rated_torque = FloatField(null=True)
   created_at = DateTimeField(default=datetime.now)

class ProductDetail(BaseModel):
   product = ForeignKeyField(Product, backref='detail', unique=True, on_delete='CASCADE')
   # ... (保留你其他的字段定义) ...
   price = FloatField(null=True)
   poles = IntegerField(null=True)
   notes = TextField(null=True, default="")

def initialize_db():
   db.connect(reuse_if_open=True)
   db.create_tables([ProductSeries, Product, ProductDetail], safe=True)
   # 确保至少有一个系列
   if ProductSeries.select().count() == 0:
       ProductSeries.create(name="默认系列", description="系统自动创建")

def close_db():
   if not db.is_closed():
       db.close()

src/data/repository.py (关键文件)

这是解决你之前报错的“门神”。它确保了无论数据库里有没有 Detail,代码都不会崩。

import logging
from typing import Optional, Dict, Any
from peewee import IntegrityError, JOIN
from .database import db, Product, ProductDetail, ProductSeries

class ProductRepository:
   """
  仓库模式:负责所有直接的数据库操作。
  UI 和 ViewModel 不应该知道 SQL 或 Peewee 的存在。
  """

   def get_all_series(self):
       return list(ProductSeries.select())

   def get_products_by_series(self, series_id: Optional[int]):
       query = Product.select()
       if series_id:
           query = query.where(Product.series == series_id)
       return list(query.order_by(Product.model_number))

   def get_product_by_id(self, product_id: int) -> Optional[Product]:
       """
      获取产品及其详情。
      ✅ 修复点:安全处理 Detail 缺失的情况。
      """
       try:
           # 1. 尝试查询
           product = (Product.select(Product, ProductDetail)
                      .join(ProductDetail, JOIN.LEFT_OUTER)
                      .where(Product.id == product_id)
                      .get())
           
           # 2. ✅ 关键修复:检查 detail 是否真的存在
           # 这里的逻辑是:如果数据库查出来 detail 是 None (因为 LEFT JOIN),
           # 我们就在内存里给它挂一个临时的空对象,防止 UI 访问报错。
           # 或者,你可以选择在这里直接插入一条空记录。
           try:
               if product.detail is None:
                   raise ProductDetail.DoesNotExist
           except ProductDetail.DoesNotExist:
               logging.warning(f"Product {product_id} missing detail. Creating default.")
               # 自动在数据库补全缺失的记录
               ProductDetail.create(product=product)
               # 重新查询以获取完整对象
               return self.get_product_by_id(product_id)

           return product
       except Product.DoesNotExist:
           return None

   def update_product(self, product_id: int, updates: Dict[str, Any]):
       """
      更新产品。
      ✅ 修复点:使用 _meta.fields 精确匹配字段。
      """
       product = self.get_product_by_id(product_id)
       if not product:
           raise ValueError("Product not found")

       detail = product.detail # get_product_by_id 保证了这里一定有值

       product_dirty = False
       detail_dirty = False

       # 获取模型字段定义
       p_fields = Product._meta.fields
       d_fields = ProductDetail._meta.fields

       for key, value in updates.items():
           if key in ('id', 'created_at', 'series', 'product'): continue

           # 更新主表
           if key in p_fields:
               if str(getattr(product, key)) != str(value):
                   setattr(product, key, value)
                   product_dirty = True
           # 更新附表
           elif key in d_fields:
               if str(getattr(detail, key)) != str(value):
                   setattr(detail, key, value)
                   detail_dirty = True

       with db.atomic():
           if product_dirty: product.save()
           if detail_dirty: detail.save()
           
       return product

2. 逻辑层 (ViewModel Layer)

src/viewmodels/product_vm.py

这是 MVVM 的核心。它持有 UI 的状态,并处理业务逻辑。

from PyQt6.QtCore import QObject, pyqtSignal
from src.data.repository import ProductRepository

class ProductViewModel(QObject):
   # --- 信号定义 ---
   series_loaded = pyqtSignal(list)       # 系列列表已加载
   products_loaded = pyqtSignal(list)     # 产品列表已加载
   current_product_changed = pyqtSignal(object) # 当前选中的产品变了 (传回 Product 对象)
   status_message = pyqtSignal(str)       # 状态栏消息
   error_occurred = pyqtSignal(str)       # 错误弹窗

   def __init__(self):
       super().__init__()
       self._repo = ProductRepository()
       self._current_product_id = None

   def load_initial_data(self):
       """初始化加载系列"""
       series = self._repo.get_all_series()
       self.series_loaded.emit(series)
       # 默认加载所有产品
       self.load_products_by_series(None)

   def load_products_by_series(self, series_id):
       """根据系列加载产品列表"""
       products = self._repo.get_products_by_series(series_id)
       self.products_loaded.emit(products)

   def select_product(self, product_id):
       """
      用户选中了某个产品 ID
      """
       self._current_product_id = product_id
       if product_id is None:
           self.current_product_changed.emit(None)
           return

       product = self._repo.get_product_by_id(product_id)
       if product:
           self.current_product_changed.emit(product)
           self.status_message.emit(f"已加载: {product.model_number}")
       else:
           self.error_occurred.emit("无法加载产品数据")

   def save_current_product(self, form_data: dict):
       """
      保存当前产品的修改
      """
       if not self._current_product_id:
           self.error_occurred.emit("未选择任何产品")
           return

       try:
           self._repo.update_product(self._current_product_id, form_data)
           self.status_message.emit("保存成功!")
           # 重新加载一次确保数据一致性
           self.select_product(self._current_product_id)
       except Exception as e:
           self.error_occurred.emit(f"保存失败: {str(e)}")

3. 界面层 (View Layer)

src/views/bldc_page.py

彻底解耦。它不知道数据库,也不知道 Repository,只认识 ViewModel。

from PyQt6.QtWidgets import (
   QWidget, QVBoxLayout, QHBoxLayout, QGroupBox, QGridLayout,
   QLabel, QLineEdit, QPushButton, QComboBox, QMessageBox
)
from PyQt6.QtCore import pyqtSlot
from src.viewmodels.product_vm import ProductViewModel

class BLDCPage(QWidget):
   def __init__(self, viewModel: ProductViewModel):
       super().__init__()
       self._vm = viewModel
       self.fields_map = {} # 存储输入框引用

       self._setup_ui()
       self._bind_view_model() # ✅ 绑定信号

   def _setup_ui(self):
       layout = QVBoxLayout(self)

       # 1. 顶部选择区
       top_layout = QHBoxLayout()
       self.series_combo = QComboBox()
       self.product_combo = QComboBox()
       self.save_btn = QPushButton("保存修改")
       
       top_layout.addWidget(QLabel("系列:"))
       top_layout.addWidget(self.series_combo)
       top_layout.addWidget(QLabel("产品:"))
       top_layout.addWidget(self.product_combo)
       top_layout.addWidget(self.save_btn)
       layout.addLayout(top_layout)

       # 2. 参数表单区 (示例)
       form_group = QGroupBox("核心参数")
       grid = QGridLayout(form_group)
       
       # 定义你要显示的字段 (Label, DB_Field_Key)
       fields = [
          ("型号", "model_number"),
          ("额定电压(V)", "rated_voltage"),
          ("额定转速(rpm)", "rated_speed"),
          ("价格($)", "price"), # 这是 Detail 表的字段,完全没问题
          ("备注", "notes")
      ]

       for i, (label, key) in enumerate(fields):
           grid.addWidget(QLabel(label), i, 0)
           edit = QLineEdit()
           self.fields_map[key] = edit
           grid.addWidget(edit, i, 1)

       layout.addWidget(form_group)
       layout.addStretch()

       # 3. 连接 UI 事件到 VM 方法
       # 注意:这里我们不写逻辑,只转发
       self.series_combo.currentIndexChanged.connect(self._on_series_changed)
       self.product_combo.currentIndexChanged.connect(self._on_product_changed)
       self.save_btn.clicked.connect(self._on_save_clicked)

   def _bind_view_model(self):
       """绑定 VM 信号到 UI 更新槽函数"""
       self._vm.series_loaded.connect(self._update_series_list)
       self._vm.products_loaded.connect(self._update_product_list)
       self._vm.current_product_changed.connect(self._display_product)
       self._vm.error_occurred.connect(lambda msg: QMessageBox.critical(self, "错误", msg))
       # 初始加载
       self._vm.load_initial_data()

   # --- UI 事件处理 ---
   def _on_series_changed(self):
       sid = self.series_combo.currentData()
       self._vm.load_products_by_series(sid)

   def _on_product_changed(self):
       pid = self.product_combo.currentData()
       self._vm.select_product(pid)

   def _on_save_clicked(self):
       # 收集数据
       data = {}
       for key, widget in self.fields_map.items():
           val = widget.text().strip()
           if val: data[key] = val
       # 发送给 VM
       self._vm.save_current_product(data)

   # --- 数据更新槽函数 ---
   @pyqtSlot(list)
   def _update_series_list(self, series_list):
       self.series_combo.blockSignals(True)
       self.series_combo.clear()
       self.series_combo.addItem("所有系列", None)
       for s in series_list:
           self.series_combo.addItem(s.name, s.id)
       self.series_combo.blockSignals(False)

   @pyqtSlot(list)
   def _update_product_list(self, product_list):
       self.product_combo.blockSignals(True)
       self.product_combo.clear()
       self.product_combo.addItem("请选择...", None)
       for p in product_list:
           self.product_combo.addItem(p.model_number, p.id)
       self.product_combo.blockSignals(False)

   @pyqtSlot(object)
   def _display_product(self, product):
       """将 Product 对象的数据填入表单"""
       if not product:
           for widget in self.fields_map.values(): widget.clear()
           return

       # 混合 product 和 detail 的属性
       # 因为我们在 repository 处理了 detail 可能为空的情况,这里可以直接访问
       detail = product.detail

       for key, widget in self.fields_map.items():
           val = None
           # 先找 Product
           if hasattr(product, key):
               val = getattr(product, key)
           # 再找 Detail
           elif hasattr(detail, key):
               val = getattr(detail, key)
           
           widget.setText(str(val) if val is not None else "")

4. 程序入口

src/main.py

import sys
from PyQt6.QtWidgets import QApplication, QMainWindow, QLabel
from src.data.database import initialize_db, close_db
from src.viewmodels.product_vm import ProductViewModel
from src.views.bldc_page import BLDCPage

class MainWindow(QMainWindow):
   def __init__(self):
       super().__init__()
       self.setWindowTitle("BLDC Motor Manager (MVVM Refactored)")
       self.resize(800, 600)
       
       # 1. 创建 ViewModel
       self.product_vm = ProductViewModel()
       
       # 2. 创建 View,并注入 VM
       self.main_view = BLDCPage(self.product_vm)
       self.setCentralWidget(self.main_view)
       
       # 3. 连接状态栏
       self.status_label = QLabel("就绪")
       self.statusBar().addWidget(self.status_label)
       self.product_vm.status_message.connect(self.status_label.setText)

if __name__ == '__main__':
   # 1. 初始化数据库
   initialize_db()
   
   # 2. 启动应用
   app = QApplication(sys.argv)
   window = MainWindow()
   window.show()
   
   try:
       sys.exit(app.exec())
   finally:
       # 3. 清理资源
       close_db()

✅ 为什么这个新版本更好?

  1. Repository 处理了“脏活累活”repository.py 里的 get_product_by_id 自动检测并修复缺失的 ProductDetail。你再也不会看到 product.detail.get() 报错了。

  2. ViewModel 统一了状态:UI 不再自己瞎改数据,而是收集数据传给 VM,VM 保存成功后再通知 UI 刷新。这保证了界面和数据库永远同步。

  3. UI 极其清爽BLDCPage 里没有一行 SQL 代码,也没有复杂的逻辑判断,只负责“显示”和“发信号”。

  4. 易于扩展:如果你想加一个新的仿真功能,只需写一个 SimulationViewModel,并在 View 里绑定它即可,完全不会影响现有的数据库逻辑。




这是一个非常棒的决定!使用 MVVM + Repository 模式重构将彻底解决你遇到的“数据不同步”、“空指针报错”以及“代码耦合”问题。

由于我无法直接接收你的文件包,我将基于你之前提供的代码片段,为你构建一个完整的、经过重构的核心代码骨架

你可以直接在你的项目中建立文件夹,复制粘贴这些代码。

📂 推荐的新项目结构

请按照这个结构整理你的文件:

src/
├── data/
│   ├── __init__.py
│   ├── database.py       # 数据库连接与模型定义 (原 model.py)
│   └── repository.py     # ✅ 核心:负责所有增删改查,隔离 SQL 细节
├── viewmodels/
│   ├── __init__.py
│   └── product_vm.py     # ✅ 核心:业务逻辑,View 和 Repository 的桥梁
├── views/
│   ├── __init__.py
│   └── bldc_page.py     # ✅ 界面:只负责显示,不处理逻辑
└── main.py               # 程序入口,依赖注入

1. 数据层 (Data Layer)

src/data/database.py

保持模型定义纯净,去掉所有业务逻辑函数。

import os
from datetime import datetime
from peewee import (
   Model, SqliteDatabase, CharField, TextField, IntegerField,
   FloatField, DateTimeField, ForeignKeyField
)

# 1. 数据库路径设置
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # 回退一级到 src
db_path = os.path.join(base_dir, "..", "products_final.db") # 假设 db 在 src 同级

# 2. 初始化数据库 (开启外键支持)
db = SqliteDatabase(db_path, pragmas={'foreign_keys': 1})

class BaseModel(Model):
   class Meta:
       database = db

class ProductSeries(BaseModel):
   name = CharField(unique=True)
   description = TextField(null=True, default="")

class Product(BaseModel):
   series = ForeignKeyField(ProductSeries, backref='products', null=True, on_delete='SET NULL')
   model_number = CharField(unique=True)
   # ... (保留你其他的字段定义,这里为了省流省略,实际代码请补全) ...
   rated_voltage = FloatField(null=True)
   rated_speed = IntegerField(null=True)
   rated_torque = FloatField(null=True)
   created_at = DateTimeField(default=datetime.now)

class ProductDetail(BaseModel):
   product = ForeignKeyField(Product, backref='detail', unique=True, on_delete='CASCADE')
   # ... (保留你其他的字段定义) ...
   price = FloatField(null=True)
   poles = IntegerField(null=True)
   notes = TextField(null=True, default="")

def initialize_db():
   db.connect(reuse_if_open=True)
   db.create_tables([ProductSeries, Product, ProductDetail], safe=True)
   # 确保至少有一个系列
   if ProductSeries.select().count() == 0:
       ProductSeries.create(name="默认系列", description="系统自动创建")

def close_db():
   if not db.is_closed():
       db.close()

src/data/repository.py (关键文件)

这是解决你之前报错的“门神”。它确保了无论数据库里有没有 Detail,代码都不会崩。

import logging
from typing import Optional, Dict, Any
from peewee import IntegrityError, JOIN
from .database import db, Product, ProductDetail, ProductSeries

class ProductRepository:
   """
  仓库模式:负责所有直接的数据库操作。
  UI 和 ViewModel 不应该知道 SQL 或 Peewee 的存在。
  """

   def get_all_series(self):
       return list(ProductSeries.select())

   def get_products_by_series(self, series_id: Optional[int]):
       query = Product.select()
       if series_id:
           query = query.where(Product.series == series_id)
       return list(query.order_by(Product.model_number))

   def get_product_by_id(self, product_id: int) -> Optional[Product]:
       """
      获取产品及其详情。
      ✅ 修复点:安全处理 Detail 缺失的情况。
      """
       try:
           # 1. 尝试查询
           product = (Product.select(Product, ProductDetail)
                      .join(ProductDetail, JOIN.LEFT_OUTER)
                      .where(Product.id == product_id)
                      .get())
           
           # 2. ✅ 关键修复:检查 detail 是否真的存在
           # 这里的逻辑是:如果数据库查出来 detail 是 None (因为 LEFT JOIN),
           # 我们就在内存里给它挂一个临时的空对象,防止 UI 访问报错。
           # 或者,你可以选择在这里直接插入一条空记录。
           try:
               if product.detail is None:
                   raise ProductDetail.DoesNotExist
           except ProductDetail.DoesNotExist:
               logging.warning(f"Product {product_id} missing detail. Creating default.")
               # 自动在数据库补全缺失的记录
               ProductDetail.create(product=product)
               # 重新查询以获取完整对象
               return self.get_product_by_id(product_id)

           return product
       except Product.DoesNotExist:
           return None

   def update_product(self, product_id: int, updates: Dict[str, Any]):
       """
      更新产品。
      ✅ 修复点:使用 _meta.fields 精确匹配字段。
      """
       product = self.get_product_by_id(product_id)
       if not product:
           raise ValueError("Product not found")

       detail = product.detail # get_product_by_id 保证了这里一定有值

       product_dirty = False
       detail_dirty = False

       # 获取模型字段定义
       p_fields = Product._meta.fields
       d_fields = ProductDetail._meta.fields

       for key, value in updates.items():
           if key in ('id', 'created_at', 'series', 'product'): continue

           # 更新主表
           if key in p_fields:
               if str(getattr(product, key)) != str(value):
                   setattr(product, key, value)
                   product_dirty = True
           # 更新附表
           elif key in d_fields:
               if str(getattr(detail, key)) != str(value):
                   setattr(detail, key, value)
                   detail_dirty = True

       with db.atomic():
           if product_dirty: product.save()
           if detail_dirty: detail.save()
           
       return product

2. 逻辑层 (ViewModel Layer)

src/viewmodels/product_vm.py

这是 MVVM 的核心。它持有 UI 的状态,并处理业务逻辑。

from PyQt6.QtCore import QObject, pyqtSignal
from src.data.repository import ProductRepository

class ProductViewModel(QObject):
   # --- 信号定义 ---
   series_loaded = pyqtSignal(list)       # 系列列表已加载
   products_loaded = pyqtSignal(list)     # 产品列表已加载
   current_product_changed = pyqtSignal(object) # 当前选中的产品变了 (传回 Product 对象)
   status_message = pyqtSignal(str)       # 状态栏消息
   error_occurred = pyqtSignal(str)       # 错误弹窗

   def __init__(self):
       super().__init__()
       self._repo = ProductRepository()
       self._current_product_id = None

   def load_initial_data(self):
       """初始化加载系列"""
       series = self._repo.get_all_series()
       self.series_loaded.emit(series)
       # 默认加载所有产品
       self.load_products_by_series(None)

   def load_products_by_series(self, series_id):
       """根据系列加载产品列表"""
       products = self._repo.get_products_by_series(series_id)
       self.products_loaded.emit(products)

   def select_product(self, product_id):
       """
      用户选中了某个产品 ID
      """
       self._current_product_id = product_id
       if product_id is None:
           self.current_product_changed.emit(None)
           return

       product = self._repo.get_product_by_id(product_id)
       if product:
           self.current_product_changed.emit(product)
           self.status_message.emit(f"已加载: {product.model_number}")
       else:
           self.error_occurred.emit("无法加载产品数据")

   def save_current_product(self, form_data: dict):
       """
      保存当前产品的修改
      """
       if not self._current_product_id:
           self.error_occurred.emit("未选择任何产品")
           return

       try:
           self._repo.update_product(self._current_product_id, form_data)
           self.status_message.emit("保存成功!")
           # 重新加载一次确保数据一致性
           self.select_product(self._current_product_id)
       except Exception as e:
           self.error_occurred.emit(f"保存失败: {str(e)}")

3. 界面层 (View Layer)

src/views/bldc_page.py

彻底解耦。它不知道数据库,也不知道 Repository,只认识 ViewModel。

from PyQt6.QtWidgets import (
   QWidget, QVBoxLayout, QHBoxLayout, QGroupBox, QGridLayout,
   QLabel, QLineEdit, QPushButton, QComboBox, QMessageBox
)
from PyQt6.QtCore import pyqtSlot
from src.viewmodels.product_vm import ProductViewModel

class BLDCPage(QWidget):
   def __init__(self, viewModel: ProductViewModel):
       super().__init__()
       self._vm = viewModel
       self.fields_map = {} # 存储输入框引用

       self._setup_ui()
       self._bind_view_model() # ✅ 绑定信号

   def _setup_ui(self):
       layout = QVBoxLayout(self)

       # 1. 顶部选择区
       top_layout = QHBoxLayout()
       self.series_combo = QComboBox()
       self.product_combo = QComboBox()
       self.save_btn = QPushButton("保存修改")
       
       top_layout.addWidget(QLabel("系列:"))
       top_layout.addWidget(self.series_combo)
       top_layout.addWidget(QLabel("产品:"))
       top_layout.addWidget(self.product_combo)
       top_layout.addWidget(self.save_btn)
       layout.addLayout(top_layout)

       # 2. 参数表单区 (示例)
       form_group = QGroupBox("核心参数")
       grid = QGridLayout(form_group)
       
       # 定义你要显示的字段 (Label, DB_Field_Key)
       fields = [
          ("型号", "model_number"),
          ("额定电压(V)", "rated_voltage"),
          ("额定转速(rpm)", "rated_speed"),
          ("价格($)", "price"), # 这是 Detail 表的字段,完全没问题
          ("备注", "notes")
      ]

       for i, (label, key) in enumerate(fields):
           grid.addWidget(QLabel(label), i, 0)
           edit = QLineEdit()
           self.fields_map[key] = edit
           grid.addWidget(edit, i, 1)

       layout.addWidget(form_group)
       layout.addStretch()

       # 3. 连接 UI 事件到 VM 方法
       # 注意:这里我们不写逻辑,只转发
       self.series_combo.currentIndexChanged.connect(self._on_series_changed)
       self.product_combo.currentIndexChanged.connect(self._on_product_changed)
       self.save_btn.clicked.connect(self._on_save_clicked)

   def _bind_view_model(self):
       """绑定 VM 信号到 UI 更新槽函数"""
       self._vm.series_loaded.connect(self._update_series_list)
       self._vm.products_loaded.connect(self._update_product_list)
       self._vm.current_product_changed.connect(self._display_product)
       self._vm.error_occurred.connect(lambda msg: QMessageBox.critical(self, "错误", msg))
       # 初始加载
       self._vm.load_initial_data()

   # --- UI 事件处理 ---
   def _on_series_changed(self):
       sid = self.series_combo.currentData()
       self._vm.load_products_by_series(sid)

   def _on_product_changed(self):
       pid = self.product_combo.currentData()
       self._vm.select_product(pid)

   def _on_save_clicked(self):
       # 收集数据
       data = {}
       for key, widget in self.fields_map.items():
           val = widget.text().strip()
           if val: data[key] = val
       # 发送给 VM
       self._vm.save_current_product(data)

   # --- 数据更新槽函数 ---
   @pyqtSlot(list)
   def _update_series_list(self, series_list):
       self.series_combo.blockSignals(True)
       self.series_combo.clear()
       self.series_combo.addItem("所有系列", None)
       for s in series_list:
           self.series_combo.addItem(s.name, s.id)
       self.series_combo.blockSignals(False)

   @pyqtSlot(list)
   def _update_product_list(self, product_list):
       self.product_combo.blockSignals(True)
       self.product_combo.clear()
       self.product_combo.addItem("请选择...", None)
       for p in product_list:
           self.product_combo.addItem(p.model_number, p.id)
       self.product_combo.blockSignals(False)

   @pyqtSlot(object)
   def _display_product(self, product):
       """将 Product 对象的数据填入表单"""
       if not product:
           for widget in self.fields_map.values(): widget.clear()
           return

       # 混合 product 和 detail 的属性
       # 因为我们在 repository 处理了 detail 可能为空的情况,这里可以直接访问
       detail = product.detail

       for key, widget in self.fields_map.items():
           val = None
           # 先找 Product
           if hasattr(product, key):
               val = getattr(product, key)
           # 再找 Detail
           elif hasattr(detail, key):
               val = getattr(detail, key)
           
           widget.setText(str(val) if val is not None else "")

4. 程序入口

src/main.py

我已经仔细阅读了你提供的所有源代码。代码量很大,逻辑比较完整,但正如之前分析的,存在**Controller 负担过重****DataService  Database 数据双重来源****View 直接处理逻辑**等架构问题。

既然你已经提供了源码,我不需要你再提供任何东西了。我将基于你现有的代码,为你生成一套**核心的重构代码**

**注意:** 为了保持回答的长度可读且可执行,我不会复制粘贴原本用于“创建布局”的几百行 UI 代码(例如 `_create_form_grid`),因为那些不需要改。我主要重写**数据流向****逻辑控制**的部分。

请按照以下步骤,在你的项目中创建文件夹并填入代码。

---

### 📂 1. 新的项目结构

请务必按照这个结构来组织文件:

```text
src/
├── main.py                     # 程序入口
├── data/                       # 数据层
  ├── __init__.py
  ├── model.py                # (原 model.py) 只保留 Class 定义,删除下面的所有函数
  └── repository.py           # ✅ 新增:负责所有增删改查
├── viewmodels/                 # 逻辑层
  ├── __init__.py
  ├── product_vm.py           # ✅ 新增:管理 CustomerRequirementsPage 的逻辑
  └── simulation_vm.py        # ✅ 新增:替代 DataService,管理 BLDCPage 和 Worker
├── views/                      # 界面层
  ├── __init__.py
  ├── main_view.py            # (原 ui/main_view.py)
  ├── design_target_page.py   # (原 design_target_page.py) 修改对接 VM
  ├── bldc_page.py            # (原 bldc_page.py) 修改对接 VM
  └── product_detail_page.py  # (原 product_detail_page.py)
└── utils/                      # 工具层 (保持你现有的不变)
   ├── worker.py
   ├── normalize.py
   └── maxwell_modules/ ...
```

---

### 🧱 2. Data Layer (数据仓库层)

#### `src/data/model.py`
**操作**:保留你原来的 `BaseModel`, `ProductSeries`, `Product`, `ProductDetail`, `initialize_database`, `close_connection`
**删除**:删除文件下方所有的 `find_product_by_id`, `create_full_product` 等函数,因为我们要把它们移到 `repository.py` 中。

#### `src/data/repository.py` (新增)
这是解决空指针报错和统一数据访问的关键。

```python
import logging
from peewee import JOIN, IntegrityError, DoesNotExist
from .model import db, Product, ProductDetail, ProductSeries

class ProductRepository:
   """负责所有数据库交互,View 和 ViewModel 不直接操作 Model"""

   def get_all_series(self):
       return list(ProductSeries.select().order_by(ProductSeries.name))

   def get_products_by_series(self, series_id):
       query = Product.select()
       if series_id and series_id != 0:
           query = query.where(Product.series == series_id)
       return list(query.order_by(Product.model_number))

   def get_product_by_id(self, product_id: int):
       """获取产品,如果详情不存在则自动修复"""
       try:
           # 左连接查询
           product = (Product.select(Product, ProductDetail)
                      .join(ProductDetail, JOIN.LEFT_OUTER)
                      .where(Product.id == product_id)
                      .get())
           
           # ✅ 自动修复逻辑:如果 detail 为空,自动创建
           # 注意:Peewee 的 LEFT JOIN 如果没查到,访问 product.detail 可能会抛错或为 None
           try:
               if product.detail is None:
                   raise DoesNotExist
           except (DoesNotExist, AttributeError):
               logging.warning(f"Product {product_id} missing detail. Auto-creating.")
               ProductDetail.create(product=product)
               # 重新查询以确保对象完整
               return self.get_product_by_id(product_id)

           return product
       except DoesNotExist:
           return None

   def create_product(self, series_id: int, model_number: str):
       """创建新产品"""
       series = ProductSeries.get_by_id(series_id)
       with db.atomic():
           product = Product.create(series=series, model_number=model_number)
           ProductDetail.create(product=product) # 必须同时创建详情
       return product

   def update_product(self, product_id: int, updates: dict):
       """更新产品,使用 _meta.fields 过滤字段"""
       product = self.get_product_by_id(product_id)
       if not product: return False

       detail = product.detail
       p_dirty, d_dirty = False, False
       
       # 字段映射
       p_fields = Product._meta.fields
       d_fields = ProductDetail._meta.fields

       for key, value in updates.items():
           if key in ('id', 'created_at', 'series', 'product'): continue
           
           # 转为字符串比较,防止 10 != 10.0 的问题
           str_val = str(value) if value is not None else ""

           if key in p_fields:
               old_val = str(getattr(product, key)) if getattr(product, key) is not None else ""
               if old_val != str_val:
                   setattr(product, key, value)
                   p_dirty = True
           elif key in d_fields:
               old_val = str(getattr(detail, key)) if getattr(detail, key) is not None else ""
               if old_val != str_val:
                   setattr(detail, key, value)
                   d_dirty = True

       with db.atomic():
           if p_dirty: product.save()
           if d_dirty: detail.save()
       return True

   def delete_product(self, product_id: int):
       Product.delete().where(Product.id == product_id).execute()
```

---

### 🧠 3. ViewModel Layer (逻辑层)

#### `src/viewmodels/product_vm.py` (新增)
这个类替代了 `Controller` 中关于“客户需求页”的所有逻辑。

```python
from PyQt6.QtCore import QObject, pyqtSignal
from src.data.repository import ProductRepository
from src.data.model import Product

class ProductViewModel(QObject):
   # 信号
   series_list_updated = pyqtSignal(list)
   product_list_updated = pyqtSignal(list)
   current_product_changed = pyqtSignal(object) # 发送 Product 对象
   status_msg = pyqtSignal(str)
   error_msg = pyqtSignal(str)

   def __init__(self):
       super().__init__()
       self.repo = ProductRepository()
       self._current_product_id = None

   def load_init_data(self):
       series = self.repo.get_all_series()
       self.series_list_updated.emit(series)
       self.filter_products_by_series(0) # 0 代表全部

   def filter_products_by_series(self, series_id):
       products = self.repo.get_products_by_series(series_id)
       self.product_list_updated.emit(products)

   def select_product(self, product_id):
       self._current_product_id = product_id
       if not product_id:
           self.current_product_changed.emit(None)
           self.status_msg.emit("就绪")
           return
       
       product = self.repo.get_product_by_id(product_id)
       if product:
           self.current_product_changed.emit(product)
           self.status_msg.emit(f"已加载: {product.model_number}")
       else:
           self.error_msg.emit("加载产品失败")

   def create_new_product(self, series_id, model_number):
       try:
           new_prod = self.repo.create_product(series_id, model_number)
           self.filter_products_by_series(series_id) # 刷新列表
           self.status_msg.emit(f"产品 {model_number} 创建成功")
           return new_prod.id
       except Exception as e:
           self.error_msg.emit(f"创建失败: {str(e)}")
           return None

   def save_changes(self, form_data: dict):
       if not self._current_product_id:
           return
       try:
           self.repo.update_product(self._current_product_id, form_data)
           self.status_msg.emit("保存成功")
           # 重新加载以刷新界面显示
           self.select_product(self._current_product_id)
       except Exception as e:
           self.error_msg.emit(f"保存失败: {str(e)}")

   def delete_current_product(self):
       if self._current_product_id:
           self.repo.delete_product(self._current_product_id)
           self.filter_products_by_series(0) # 刷新
           self.select_product(None)
           self.status_msg.emit("产品已删除")
```

#### `src/viewmodels/simulation_vm.py` (新增)
这个类替代了 `DataService` `bldc_page` 中的逻辑部分。它实现了**单一数据源**

```python
from PyQt6.QtCore import QObject, pyqtSignal, pyqtSlot
from src.utils.worker import MaxwellWorker
from src.utils.maxwell_modules.maxwell_config import MaxwellConfig
from src.data.model import Product
from pathlib import Path

class SimulationViewModel(QObject):
   # 信号
   parameter_changed = pyqtSignal(str, object) # key, value
   log_received = pyqtSignal(str)
   progress_updated = pyqtSignal(int)
   simulation_finished = pyqtSignal(object) # fig
   simulation_error = pyqtSignal(str)

   def __init__(self):
       super().__init__()
       # 替代 DataService,这里是所有参数的唯一真实来源
       self.parameters = {}
       self.worker = MaxwellWorker()
       
       # 连接 Worker 信号
       self.worker.log_message.connect(self.log_received)
       self.worker.progress_updated.connect(self.progress_updated)
       self.worker.finished.connect(self.simulation_finished)
       self.worker.error.connect(self.simulation_error)

   def load_product(self, product: Product):
       """当用户在主界面切换产品时调用"""
       self.parameters.clear()
       if not product: return

       # 从 Product 对象加载数据到 parameters 字典
       # 这里你可以复用 normalize.py 或者手动映射
       d_dict = product.detail.__data__ if product.detail else {}
       p_dict = product.__data__
       
       # 合并并存入 self.parameters
       self.parameters.update(p_dict)
       self.parameters.update(d_dict)
       
       # 触发 UI 更新(如果有需要的话,或者 View 直接监听 ProductVM)
       
   def update_parameter(self, key, value):
       """UI 输入框变化时调用"""
       self.parameters[key] = value
       # 这里可以加逻辑:如果 value 变了,是否需要通知其他地方?

   def start_simulation(self, project_path, project_name):
       if self.worker.isRunning():
           return

       config = MaxwellConfig(project_path, Path(project_path).parent,
                              project_name, "RMxprtDesign", "Setup")
       
       # 准备数据:self.parameters 就是最新的数据,因为 input 改动时同步更新了
       self.worker.start()
       self.worker.execute_simulation_task(config, self.parameters)

   def cleanup(self):
       self.worker.cleanup()
```

---

### 🎨 4. View Layer (界面层改造)

#### `src/views/design_target_page.py` (改造要点)

1.  **构造函数**:接收 `ProductViewModel`
2.  **删除**:删除 `DataService`
3.  **连接**
   *   下拉框 `currentIndexChanged` -> 调用 `vm.select_product(id)`
   *   保存按钮 `clicked` -> 收集 `self.fields_map` 数据 -> 调用 `vm.save_changes(data)`
   *   VM 信号 `current_product_changed` -> 触发 `load_data_to_form(product)`

```python
# 代码片段示例
class CustomerRequirementsPage(QWidget):
   def __init__(self, viewModel):
       super().__init__()
       self.vm = viewModel
       # ... UI 初始化 ...
       
       # 绑定
       self.vm.product_list_updated.connect(self.update_product_combo_box)
       self.vm.current_product_changed.connect(self.load_data_to_form)
       
       self.save_button.clicked.connect(self._on_save)

   def _on_save(self):
       data, _ = self.get_data_from_form() # 复用你现有的收集逻辑
       # 把两个字典合二为一传给 VM
       full_data = {**data[0], **data[1]}
       self.vm.save_changes(full_data)
```

#### `src/views/bldc_page.py` (改造要点)

1.  **构造函数**:接收 `SimulationViewModel`
2.  **删除**:删除 `DataService`, `MaxwellWorker` (因为移到了 VM)
3.  **修改输入框**`textChanged` 事件不再更新 `DataService`,而是调用 `self.vm.update_parameter(key, value)`
4.  **修改开始按钮**:调用 `self.vm.start_simulation(...)`

```python
# 代码片段示例
class MaxwellApp(QWidget):
   def __init__(self, sim_vm):
       super().__init__()
       self.vm = sim_vm
       # ... UI 初始化 ...
       
       # 绑定
       self.vm.log_received.connect(self.log_output.append)
       self.vm.simulation_finished.connect(self._on_sim_finished)
       
   def _on_text_changed(self, key, text):
       # ... 类型转换 ...
       self.vm.update_parameter(key, value)
       
   def _start_simulation(self):
       # ... 检查路径 ...
       self.vm.start_simulation(self.selected_project_path, self.project_name)
```

---

### 🚀 5. Main Entry (入口)

#### `src/main.py`
这是组装所有组件的地方。

```python
import sys
from PyQt6.QtWidgets import QApplication
from src.data.model import initialize_database, close_connection
from src.viewmodels.product_vm import ProductViewModel
from src.viewmodels.simulation_vm import SimulationViewModel
from src.views.main_view import MainView

def main():
   app = QApplication(sys.argv)
   
   # 1. 初始化 DB
   initialize_database()
   
   # 2. 创建 ViewModels
   product_vm = ProductViewModel()
   sim_vm = SimulationViewModel()
   
   # 3. 关联 VM:当产品改变时,通知仿真 VM 加载新数据
   product_vm.current_product_changed.connect(sim_vm.load_product)
   
   # 4. 创建 MainView (需要修改 MainView 构造函数来接收 VMs)
   #   或者 MainView 创建子页面时,把 vm 传进去
   main_window = MainView(product_vm, sim_vm)
   main_window.show()
   
   # 5. 初始加载数据
   product_vm.load_init_data()
   
   exit_code = app.exec()
   
   # 6. 清理
   sim_vm.cleanup()
   close_connection()
   sys.exit(exit_code)

if __name__ == "__main__":
   main()
```

### 修改 `MainView` (src/views/main_view.py)

你需要稍微改一下 `MainView` `__init__` `_create_main_content_stack`

```python
class MainView(QMainWindow):
   def __init__(self, product_vm, sim_vm): # 接收 VMs
       super().__init__()
       self.product_vm = product_vm
       self.sim_vm = sim_vm
       # ... 其他初始化 ...
       self.main_content_stack = self._create_main_content_stack()
       # ...

   def _create_main_content_stack(self):
       stack = QStackedWidget()
       # 注入 VM 到子页面
       self.customer_page = CustomerRequirementsPage(self.product_vm)
       self.bldc_page = MaxwellApp(self.sim_vm)
       self.detail_page = ProductDetailPage() # 这个页面比较简单,可以后续再加绑定
       
       stack.addWidget(self.customer_page)
       stack.addWidget(self.bldc_page)
       stack.addWidget(self.detail_page)
       return stack
```

### 总结

这套方案直接使用了你提供的 Peewee Model 定义和 PyQt 界面代码,但重构了**连接方式**

1.  **Repository** 帮你自动处理了 `product.detail` 为空的情况,消灭了空指针 BUG。
2.  **ProductViewModel** 统一了数据库读写逻辑,不再依赖 `controller.py`
3.  **SimulationViewModel** 统一了仿真参数,替代了 `DataService`,解决了“数据不同步”的问题。
4.  **MainView** 负责依赖注入,让各个组件松耦合。

✅ 为什么这个新版本更好?

  1. Repository 处理了“脏活累活”repository.py 里的 get_product_by_id 自动检测并修复缺失的 ProductDetail。你再也不会看到 product.detail.get() 报错了。

  2. ViewModel 统一了状态:UI 不再自己瞎改数据,而是收集数据传给 VM,VM 保存成功后再通知 UI 刷新。这保证了界面和数据库永远同步。

  3. UI 极其清爽BLDCPage 里没有一行 SQL 代码,也没有复杂的逻辑判断,只负责“显示”和“发信号”。

  4. 易于扩展:如果你想加一个新的仿真功能,只需写一个 SimulationViewModel,并在 View 里绑定它即可,完全不会影响现有的数据库逻辑。







n5321 | 2025年11月22日 00:25

AutoEM coreparameter update

目标:增加核心参数的一次性罗列!在每一次call Ansys maxwell 完成计算之后,获取结果参数,call back 到UI上!

方式:核心参数还是用qgrid生成,保持动态特性。价值是维护方便

数据存在df_for_detail 的变量里面!

暂时的问题是对数据结构的设计,需要呈现哪些data还需要仔细的思考!



n5321 | 2025年11月2日 23:29

AutoEM chart update

一个logic 看上去很简单的update,居然花了差不多两天的时间!

目标:在matplotlib的曲线图里添加对照组。

方式:在UI里添加button,选择,读取csv文档 add traces,沿用原始的chart设置!

测试idea: 写了一个try_compare.py,UI, path齐全,分分钟搞定了!

实操:添加进AutoEM。

完全不是那么回事!

try_compare写的logic 跟AutoEM完全不同!try_compare在一段script把所有的东西都包括进去了,所以debug 也极其简单!

在AutoEM中有一个很复杂的操作,因为数据和控制要传到到N个object!他具体的logi大概如下:

  1. page默认生成一个chart! chart是在fig 里面!

  2. UI提供chart的框子——准备填matplot的figure,设置成canva

  3. worker get data

  4. plotting tool 升成figure(用新曲线完全替代)

  5. 再返回到worker

  6. UI清楚原来的figure,添加新的figure

尝试实操!

  1. 更改logic!不在替换figure,考虑用同一个figure,在figure的框架里做删改traces!

    1. 问题是figure里面对于chart的设置有大量的代码,把设置代码从plotting_tool改到init之中,后续recall的时候又会用默认设置。

    2. 放弃新idea,继续用figure替换的思路。

  2. 未做深入思考,考虑增加一个function,plotting_tool生成sim chart,增加一个ref_chart的功能几乎一致的function,来生成ref chart。然后卡住了。

    1. 晚上睡觉才想起本质上是替换的,把设置全部堆到plotting_tool才是正常操作!

  3. 增加新功能,UI里添加button,click以后,可以看到ref chart!

    1. 需要把UI get 到的data 传递到worker,worker生成fig,再传递会UI

    2. 本质上我对signal and slot的逻辑没有搞清楚。

  4. 问题:在这种细节繁复的装配体结构中,数据的传递,logic线的设计,是架构师内容。还蛮麻烦的!

  5. 自己对QT的框架理解本质上不够!

还是需要深入阅读代码!

暂时解决问题!


n5321 | 2025年11月2日 10:42

maxwell 作为com对象分析

matlab 有invoke 函数来探测COM!

在python 下面用的dir

dir(self.maxwell_app)
['Design', 'Desktop', 'Editor', 'Module', 'Project', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'analyze_design', 'connect_maxwell', 'export_reports', 'iMaxwell', 'open_project_and_design', 'release_maxwell', 'set_properties']

具体来看:

 MaxwellAutomation 类结构概览

🧩 一、核心对象属性(Attributes)

属性名类型/用途说明
DesktopAEDT 桌面对象(oDesktop)控制 Maxwell AEDT 会话、项目打开、退出等操作。
ProjectAEDT 工程对象(oProject)当前打开的 Maxwell 工程。
DesignAEDT 设计对象(oDesign)当前工程下的具体设计,如“RMxprtDesign”或“Maxwell3DDesign”。
EditorAEDT 编辑器对象(oEditor)用于修改几何、材料、属性、参数。通常是 "3D Modeler" 编辑器。
ModuleAEDT 模块对象(oModule)可调用报告、求解、场后处理等功能模块,如 "ReportSetup""FieldsReporter" 等。
iMaxwellCOM 接口对象win32com.client.Dispatch("Ansoft.ElectronicsDesktop") 获取,用于连接 AEDT。

⚙️ 二、主要方法(Methods)

方法名主要功能典型调用示例
__init__()初始化 MaxwellAutomation 实例app = MaxwellAutomation()
connect_maxwell()建立与 Maxwell AEDT 的 COM 连接app.connect_maxwell()
open_project_and_design(config)打开指定工程与设计app.open_project_and_design(config)
set_properties(property_changes)批量修改参数属性app.set_properties(property_changes)
analyze_design(setup_name)启动指定 Setup 的仿真app.analyze_design("Setup1")
export_reports(config)导出仿真结果报告或数据文件app.export_reports(config)
release_maxwell()关闭并释放 Maxwell 会话app.release_maxwell()

🧱 三、内置 Python 魔法方法(不需手动调用)

这些是 Python 自动提供的类方法,用于对象描述、比较、属性访问等,一般不手动使用:

方法功能
__init__构造函数
__str__ / __repr__控制打印输出格式
__eq__, __lt__, __gt__对象比较
__getattribute__, __setattr__控制属性访问
__dict__, __doc__存放实例属性与文档说明
__sizeof__, __reduce__, __module__内部运行支持


n5321 | 2025年10月30日 22:20

Python modify Ansys Maxwell properties!

python通过com32 来更改ansys 参数的logic 跟matlab 确实不同!

一个简单的事情,居然还花了几个小时的时间!

背景:

1. matlab基本上copy record vbscript 就可以了!但是python 用的是不一样的logic!
通过com32, 他是直接插入到Ansys对象里面去的!一个com又已经被封装了!  我熟悉的是actxserver下面对ansys 的manipulate!

2. Ansys Maxwell下面的数据结构对于不同的参数来说,是不一样的!所以他需要设计不同的控制结构!开始考虑是做成一个数组结构,然后添加索引,match 到了以后直接调用。

问题是中间debug的过程比较麻烦!因为封装了,所以有点难把问题点拎出来!

logic! 暂时用的这个code 实现!

def set_properties(self, prop_changes):
"""Applies a list of property changes to the active design or editor."""
for prop_change in prop_changes:
changed_props = ["NAME:" + prop_change['name']]
print('更改的参数', changed_props)

if prop_change['name'] == 'Wire Size':
# Wire Size 复合属性
changed_props += [
"WireSizeWireDiameter:=", prop_change['value'],
"WireSizeGauge:=", "0",
"WireSizeWireWidth:=", "0mm",
"WireSizeWireThickness:=", "0mm",
"WireSizeMixedWireRectType:=", False,
["NAME:WireSizeMixedDiameter"],
["NAME:WireSizeMixedWidth"],
["NAME:WireSizeMixedThickness"],
["NAME:WireSizeMixedThicknessMixedFillet"],
["NAME:WireSizeMixedThicknessMixedNumber"]
]
print('wiresize data is', changed_props)

# 不同类型的参数修改方式
if 'type' in prop_change:
if prop_change['type'] == 'Value':
changed_props += ["Value:=", prop_change['value']]
elif prop_change['type'] == 'Material':
changed_props += ["Material:=", prop_change['value']]
elif prop_change['type'] == 'MustBeInt':
changed_props += [
"MustBeInt:=", prop_change.get('must_be_int', False),
"Value:=", prop_change['value']
]
else:
# 默认只写 Value
changed_props += ["Value:=", prop_change['value']]

# 选择目标对象
if prop_change['target'] == 'Editor':
self.Editor.ChangeProperty([
"NAME:AllTabs",
[
f"NAME:{prop_change['tab']}",
["NAME:PropServers", prop_change['prop_server']],
["NAME:ChangedProps", changed_props]
]
])
elif prop_change['target'] == 'Design':
self.Design.ChangeProperty([
"NAME:AllTabs",
[
f"NAME:{prop_change['tab']}",
["NAME:PropServers", prop_change['prop_server']],
["NAME:ChangedProps", [
"NAME:" + prop_change['name'],
"Value:=", prop_change['value']
]]
]
])

几个点记录一下!

1, 考虑更改的参数,暂时用的prop_changes来统一。里面去changed_props来做微调。

最后通过self.editer or self.design来更改。

2, 更改的参数点,需要把所以的参数都记录进来!


更改文档
maxwell_automation.py

ai_search_page.py




 


n5321 | 2025年10月26日 11:00