“雖然有點(diǎn)復(fù)古,但好玩的項(xiàng)目永不過時(shí)。Nixie Tube Audio Meter(輝光管音頻電平表)是一種結(jié)合復(fù)古輝光管顯示技術(shù)與現(xiàn)代音頻處理功能的電子設(shè)備,以蒸汽朋克美學(xué)的形式可視化音頻信號(hào)的動(dòng)態(tài)變化”





高中時(shí)期我曾用霓虹數(shù)碼管制作過一個(gè)時(shí)鐘。那是個(gè)非常原始的結(jié)構(gòu):布滿手工接線、實(shí)驗(yàn)板和自制蝕刻PCB。令人驚訝的是它居然運(yùn)行良好!但最終成品實(shí)在不夠美觀,導(dǎo)致我從未實(shí)際使用過它——粗糙切割和膠合的預(yù)制塑料外殼所帶來的負(fù)面美學(xué)效應(yīng),完全抵消了霓虹數(shù)碼管本身的視覺魅力。
另一個(gè)高中時(shí)期就想實(shí)現(xiàn)但受限于資金而未能完成的項(xiàng)目,是使用前蘇聯(lián)的柱狀顯示管制作光譜儀風(fēng)格的音頻表。這些高壓氣體放電管能根據(jù)輸入電流大小顯示不同高度的垂直光柱。
我認(rèn)為現(xiàn)在是時(shí)候重新審視這個(gè)擱置多年的創(chuàng)意了。所有代碼與原理圖均已開源至GitHub。
本文內(nèi)容分為以下幾個(gè)部分:
-
硬件篇:詳解電路設(shè)計(jì)。包含KiCad設(shè)計(jì)軟件使用、線纜連接等內(nèi)容。
-
軟件篇:解析系統(tǒng)運(yùn)行代碼。涵蓋(嵌入式)Rust編程語言、數(shù)字信號(hào)處理(DSP)算法等實(shí)現(xiàn)細(xì)節(jié)。
-
外殼篇:介紹設(shè)備外殼的設(shè)計(jì)與制作過程。


關(guān)于3D建模與渲染的更多細(xì)節(jié)詳見外殼篇
結(jié)合其豐富的元件庫資源、以及自定義元件添加/編輯的流暢體驗(yàn),堪稱EDA領(lǐng)域的重要贏家。
電路系統(tǒng)概覽:
-
霓虹管驅(qū)動(dòng)板
-
模塊化菊花鏈?zhǔn)皆O(shè)計(jì),每板支持4根數(shù)碼管
-
核心適配IN-13霓虹數(shù)碼管,同時(shí)兼容更經(jīng)濟(jì)的IN-9管
-
采用TPS2281電源管理IC控制高壓電源啟停,無操作數(shù)秒后自動(dòng)斷電以延長數(shù)碼管壽命
-
170V高壓由NCH8200HV模塊生成。根據(jù)數(shù)碼管能效差異(IN-13單模塊即可驅(qū)動(dòng),IN-9需雙模塊)支持1-2模塊靈活配置
-
每根數(shù)碼管可通過恒流源微調(diào)電位器單獨(dú)校準(zhǔn)
-
板間通過DIP跳線連接
-
-
支撐架板(原理圖1,原理圖2)
-
安裝在驅(qū)動(dòng)板上方,負(fù)責(zé)數(shù)碼管定位與垂直固定
-
頂層支撐板采用橡膠墊圈固定數(shù)碼管
-
-
控制主板
-
-
獨(dú)立于數(shù)碼管驅(qū)動(dòng)板運(yùn)行,負(fù)責(zé)音頻信號(hào)處理與電平顯示決策
-
自動(dòng)檢測連接數(shù)碼管數(shù)量并匹配顯示頻段
-
基礎(chǔ)配置僅需5-12V電源+3.3V UART,理論上可用USB轉(zhuǎn)串口模塊簡化實(shí)現(xiàn)
-
本方案實(shí)現(xiàn)版本支持Toslink光纖S/PDIF音頻輸入
-
編程語言采用Rust與C混合開發(fā)
-
數(shù)碼管驅(qū)動(dòng)板原理圖預(yù)覽:



2024 年補(bǔ)記(本文最早是2020年發(fā)布的):。如今Rust異步生態(tài)與裸機(jī)調(diào)度器(如RTIC與Embassy框架)已大幅成熟,下文所述方案可能不再具有實(shí)用價(jià)值。建議探索基于調(diào)度器的新體系。
在嵌入式開發(fā)(乃至廣義軟件開發(fā))中,我堅(jiān)定推崇事件驅(qū)動(dòng)架構(gòu)。對(duì)于嵌入式場景,這意味著構(gòu)建一個(gè)主循環(huán)(master loop),持續(xù)監(jiān)聽輸入事件(引腳狀態(tài)變化、定時(shí)器觸發(fā)、串口數(shù)據(jù)接收等),并在事件觸發(fā)時(shí)更新內(nèi)部狀態(tài)、執(zhí)行輸出動(dòng)作(設(shè)置引腳電平、發(fā)送串口數(shù)據(jù)等)。
避免使用阻塞式延時(shí)函數(shù)。推薦在后臺(tái)運(yùn)行一個(gè)固定間隔的定時(shí)器,通過統(tǒng)計(jì)定時(shí)器觸發(fā)次數(shù)來管理周期性任務(wù)。這種方式能確保主循環(huán)在執(zhí)行任務(wù)間隙仍可處理其他事件。
主循環(huán)不應(yīng)以100% CPU占用率空轉(zhuǎn)輪詢。多數(shù)嵌入式平臺(tái)提供「事件等待」機(jī)制——即進(jìn)入低功耗睡眠模式,直至相關(guān)硬件事件觸發(fā)中斷喚醒。ARM架構(gòu)中的wfi
指令("等待中斷")正是此類機(jī)制的體現(xiàn)。只要確保所有關(guān)鍵硬件事件均關(guān)聯(lián)中斷(或存在足夠多的隨機(jī)中斷保證主循環(huán)及時(shí)響應(yīng)),輪詢過程就能最大限度減少無效能耗。
在嵌入式應(yīng)用中,我的主循環(huán)通常會(huì)如下所示:
enumPinEvent{
PinTurnedOn,PinTurnedOff
}
enumTimerEvent{
TimerFired{times:usize}
}
enumSerialEvent{
ByteReceived(u8)
}
// We have a single master event type which wraps all other events
enumEvent{
Pin(PinEvent),Timer(TimerEvent),Serial(SerialEvent)
}
impl Pins {
fnupdate(&mutself) ->Option<PinEvent>{/* ... */}
}
impl Timer {
fnupdate(&mutself) ->Option<TimerEvent>{/* ... */}
}
impl Serial {
fnupdate(&mutself) ->Option<SerialEvent>{/* ... */}
}
loop {
// Check each piece of hardware for a change.
// Instead of using this polling based model, you can also
// use the interrupt handlers to e.g. put events into
// some event buffers and read from those buffers.
let pin_event = input_pins.update().map(Event::Pin);
let timer_event = timer.update().map(Event::Timer);
// In many cases, it's better to process multiple inputs
// per loop, usually by passing around a buffer. See my
// S/PDIF code for an example - it will process up to 128
// samples per cycle.
let serial_event = serial.update().map(Event::Serial)
// We have a master state object which, internally,
// keeps mutable reference(s) to the state of our logic.
// We pass in all hardware events as well as mutable handle
// objects that the state object can use to control hardware.
//
// Hardware handles should not have a concrete type in the state definition. There
// should be a trait that describes the capabilities of the hardware and a type
// parameter constrained to satisfy that trait. This allows you to mock out
// hardware during testing.
//
// Sometimes you'll want to pass in the hardware handle at every update() invocation,
// and sometimes you'll want to have the state object own the handle. Either way,
// use a trait to define the capability of the hardware.
//
// Fixed-sized output events (e.g. LED on/off) can optionally be returned from the
// state update function, but for variable-sized output events it's usually easier
// to pass in some object which consumes the events (e.g. tx_buffer).
forevent in [pin_event,timer_event,serial_event].iter() {
state.update(event, &mut tx_buffer, &mut led);
}
// Iterating like this may or may not work for your Event type.
// You may want to use pin_event.into_iter().chain(...) instead of putting
// the events in an array and iterating over that.
// Flush as much buffered data (e.g. outgoing serial bytes)
// as possible (without blocking) to hardware.
// It's important to keep in mind that the design principles here
// strongly suggest that you shouldn't ever block waiting for the
// UART to flush. Instead, you should intentionally decide when (not)
// to send based on available UART bandwidth. In this project,
// I just drop packets if I'm low on bandwidth (which I try to avoid).
tx_buffer.flush();
// If we're not too busy, execute wfi (or equivalent).
// Processor will go into a low-power state
// until an interrupt fires.
// We should be confident that an interrupt will fire
// every time something we care about changes, or at least
// that some arbitrary interrupt will happen frequently
// enough that our loop runs sufficiently often.
let not_busy = [pin_event,timer_event,serial_event].iter().all(|evt| evt.is_none());
ifnot_busy {wfi()}
}
對(duì)于這樣的嵌入式程序,有幾點(diǎn)建議:
-
規(guī)避阻塞操作。阻塞式代碼會(huì)嚴(yán)重阻礙功能擴(kuò)展,尤其在需要并發(fā)處理時(shí)
-
替代方案:采用定時(shí)器/計(jì)數(shù)器機(jī)制替代延時(shí)函數(shù)
-
-
如果需要管理功耗,啟用基于中斷的休眠機(jī)制,實(shí)現(xiàn)零功耗空轉(zhuǎn)等待
-
禁止動(dòng)態(tài)內(nèi)存分配:嵌入式場景中動(dòng)態(tài)內(nèi)存分配易引發(fā)致命錯(cuò)誤。Rust嵌入式生態(tài)對(duì)此有深刻認(rèn)知,標(biāo)準(zhǔn)庫提供優(yōu)秀靜態(tài)內(nèi)存管理方案
-
確保狀態(tài)表示正確且方便使用。采用聯(lián)合類型(sum types)精確描述系統(tǒng)狀態(tài)(Rust的enum類型在此大顯身手)
-
設(shè)計(jì)時(shí)注意狀態(tài)模型的易操作性,避免與硬件直接耦合
-
盡量使用純函數(shù)(即不使用可變性)。這樣可以更容易地推理應(yīng)用程序的行為,尤其是在重構(gòu)過程中。在沒有分配器和垃圾回收器的情況下,這可能會(huì)比較困難,但通常還是值得一做。
-
盡量將硬件修改與事件處理分開。
-
嘗試將事件建模為數(shù)據(jù)。與其在應(yīng)用程序邏輯中間檢查一堆硬件標(biāo)志來弄清硬件發(fā)生了什么變化,不如嘗試將其抽象為一種方便的事件類型,并為每種可能的事件提供不同的構(gòu)造函數(shù)。例如,在我處理 S/PDIF 的 Rust 代碼中,我有一個(gè)事件類型來描述 S/PDIF 硬件可能發(fā)生的變化:pub enum Event { LockLost, LockAcquired(f32), Samples(T)}。在我的應(yīng)用邏輯中,我只需在這個(gè)方便的事件類型上進(jìn)行模式匹配,而不必去處理 S/PDIF 硬件中的所有寄存器。
-
-
-
在 Rust 環(huán)境中,lifetime 系統(tǒng)和 impl 返回值對(duì)處理這類問題很有幫助。
-
其中很多建議與我在文章《Haskell 中的分布式系統(tǒng)》(Distributed Systems in Haskell)中給出的建議非常相似;其中很多想法也適用于嵌入式環(huán)境之外。
Rust 實(shí)戰(zhàn)體驗(yàn)在本項(xiàng)目中,我深度運(yùn)用了Rust編程語言。盡管在日常軟件開發(fā)中我鮮少使用Rust(畢竟在非實(shí)時(shí)操作系統(tǒng)場景下,垃圾回收語言通常更高效),但Rust在嵌入式領(lǐng)域展現(xiàn)出非凡魅力。其特性與嵌入式開發(fā)需求高度契合:
-
零動(dòng)態(tài)內(nèi)存分配:規(guī)避動(dòng)態(tài)內(nèi)存管理的所有潛在風(fēng)險(xiǎn)
-
安全指針操作:借助生命周期系統(tǒng)實(shí)現(xiàn)C/C++難以企及的內(nèi)存安全
-
硬件獨(dú)占訪問建模:通過線性類型系統(tǒng)(Linear Types)精確控制硬件資
和C(++)相比,Rust具有以下現(xiàn)代化語言特性:
-
內(nèi)存安全保證
-
代數(shù)數(shù)據(jù)類型(Algebraic Data Types)
-
參數(shù)多態(tài)(Parametric Polymorphism,雖有限但實(shí)用)
ARM生態(tài)支持
Rust對(duì)ARM架構(gòu)的支持相當(dāng)完善,主流開發(fā)板大多擁有成熟的純Rust驅(qū)動(dòng)庫。若現(xiàn)有庫無法滿足需求,Rust的C FFI接口可便捷調(diào)用C代碼。
但在項(xiàng)目過程中我也遇到以下痛點(diǎn):
-
類型系統(tǒng)局限:
? 缺乏高階種類多態(tài)(Higher-Kinded Polymorphism)? 不支持二階類型變量(Rank-2 Type Variables)
? 缺失尺寸多態(tài)(Size Polymorphism)
? 無法在
where
子句中添加等式約束(Equality Constraints) -
狀態(tài)建模挑戰(zhàn):
? 不可臨時(shí)移出可變引用(因與panic!
宏的交互未達(dá)成共識(shí))? Trait 中無法返回
impl
類型 -
異步生態(tài)短板:
? 無標(biāo)準(zhǔn)庫時(shí)需依賴Nightly版封裝包實(shí)現(xiàn)async/await
? 特質(zhì)定義中禁用異步函數(shù)(與
impl
返回值缺失形成雙重打擊)
上述痛點(diǎn)多源于類型系統(tǒng)泛化不足,有望隨語言演進(jìn)逐步改善。部分特性已進(jìn)入活躍開發(fā)階段。總體而言,相較于C/C++,Rust的嵌入式開發(fā)體驗(yàn)堪稱愉悅。畢竟,誰不想在寫驅(qū)動(dòng)代碼時(shí)享受模式匹配與內(nèi)存安全的雙重福利呢?
音頻處理音頻處理流程如下:

帶通濾波器由N個(gè)指數(shù)間距分布的雙二階帶通濾波器構(gòu)成,其中 N 是電子管的數(shù)量。各頻段能量計(jì)算后輸入指數(shù)加權(quán)移動(dòng)平均濾波器(EWMA)實(shí)現(xiàn)響應(yīng)平滑。然后,我們將左右兩組 EWMA 濾波器相加,并將其送入一個(gè)中等復(fù)雜的算法,該算法試圖找到一個(gè)從能量水平到輝光管高度的既美觀又實(shí)用的映射關(guān)系。簡單來說,它是根據(jù)各通道隨時(shí)間變化的能級(jí)分布,試圖找到一個(gè) “有代表性 ”的能級(jí),并將其作為給定動(dòng)態(tài)范圍對(duì)數(shù)標(biāo)度表示法的參考點(diǎn)。
該系統(tǒng)的輸出是一個(gè)由 [0, 1] 中的 N 個(gè)值組成的數(shù)組,每 M 個(gè)輸入樣本出現(xiàn)一次(M 可以是任意正整數(shù))。
包協(xié)議(Packet Protocol)電路板使用簡單的基于數(shù)據(jù)包的協(xié)議進(jìn)行通信。每個(gè)數(shù)據(jù)包由 6 個(gè)字節(jié)組成:1 字節(jié)報(bào)頭、1 字節(jié) TTL(用于選擇受控的燈管)、2 字節(jié)亮度級(jí)別和 2 字節(jié)校驗(yàn)和。
每當(dāng)電路板接收到一個(gè)數(shù)據(jù)包,它就會(huì)檢查 TTL t 是否小于 4。如果 t 大于 4,則從 t 中減去 4,然后重新轉(zhuǎn)發(fā)數(shù)據(jù)包。
控制板也可以使用此協(xié)議來檢測燈管的數(shù)量。我們使用一個(gè)跳線將鏈條中的最后一塊電路板與自己連接起來,這樣就可以將信息 “反射” 回控制板,并將 TTL 減去燈管數(shù)量的 2 倍。
輝光管板子輝光管板子處于輪詢狀態(tài),等待幾種情況中的一種發(fā)生:
-
如果 UART 收到信息,它就會(huì)
-
打開輝光管(如果它們處于關(guān)閉狀態(tài))并應(yīng)用信息中指定的亮度(使用 PWM 引腳之一)
-
將信息轉(zhuǎn)發(fā)到另一個(gè) UART(如果信息是針對(duì)不同電路板的)
-
-
如果 3 秒內(nèi)沒有收到任何信息,則關(guān)閉燈管。假定它已與控制板斷開連接。
-
如果所有電子管在 10 秒內(nèi)都設(shè)置為零,則會(huì)關(guān)閉電子管。假定音樂(或其他)已經(jīng)暫停。
在靜止期間關(guān)閉電子管的主要好處是降低功耗和減少對(duì)輔助陰極的磨損。電子管電路板還具有邏輯功能,可以讓 IN-13 電子管上的輔助陰極在激活主陰極之前有足夠的時(shí)間激活,從而提高電子管的可靠性。
音頻板子對(duì)于音頻板(對(duì)音頻信號(hào)進(jìn)行 DSP 處理,并將數(shù)值發(fā)送到輝光管板子),可以嘗試以下兩種方案。方案一:樹莓派+擴(kuò)展板第一種設(shè)計(jì)基于 Raspberry Pi 和 Hat,增加了對(duì)數(shù)字和模擬音頻輸入的支持。不幸的是,由于多種原因,這種設(shè)計(jì)效果不佳:
-
Pi 尺寸較大
-
Pi 加上 Hat 和各種配件比其他方案昂貴得多
-
我找到的唯一合適的 Hat 基本上沒有制造商支持,需要使用專有 Windows IDE 才能正確配置
-
Pi 的功耗非常高,而且電壓不方便
-
Pi 需要很長時(shí)間才能啟動(dòng)
-
Pi 需要特殊配置才能保障可靠性(例如啟用 OverlayFS 以防止磁盤寫入)
-
Pi 采用非實(shí)時(shí)操作系統(tǒng),具有大量緩沖區(qū),會(huì)帶來不可忽略的延遲
所以這個(gè)設(shè)計(jì)是失敗的。
方案二:Teensy 4 開發(fā)板第二種方案的結(jié)果要好得多,使用 “Teensy”開發(fā)板(第 4 版)。這是一塊簡單而廉價(jià)的電路板,配備一個(gè) 600MHz 的 32 位 ARM 處理器。在我的使用案例中,制造商支持水平大致也相當(dāng)于沒有,但基本上在所有其他指標(biāo)上都優(yōu)于 Pi。在進(jìn)行 DSP 處理時(shí),它甚至在處理器使用率方面擊敗了 Pi。這在我看來不太對(duì)勁,也許是 Pi 的音頻驅(qū)動(dòng)程序帶來了大量開銷或其他什么。
Teensy 的一個(gè)缺點(diǎn)是對(duì) Rust 的支持非常有限。有一個(gè)軟件包可以讓你啟動(dòng) Teensy、連接到中斷處理程序、通過 USB 記錄信息等,但它并不真正支持任何外設(shè)。我不得不用 C 語言完成所有外設(shè),并通過 FFI 從 Rust 調(diào)用 C 語言。我嘗試過另一種方法(將 Rust DSP/協(xié)議代碼編譯成靜態(tài)鏈接庫,然后從支持良好的 Arduino Teensy 設(shè)置中調(diào)用),但如果你試圖做任何類似的復(fù)雜事情,Arduino IDE 就會(huì)懲罰你,而且我無法讓它正確鏈接。
音頻板啟動(dòng)時(shí),它會(huì)自動(dòng)檢測連接了多少個(gè)電子管。電子管數(shù)量用于配置音頻表參數(shù)(帶通濾波數(shù)量、刷新率等)。
然后,音頻板將處于輪詢狀態(tài),等待下列情況之一發(fā)生:
-
如果 S/PDIF PLL 時(shí)鐘穩(wěn)定在一個(gè)時(shí)鐘頻率上(即建立了 S/PDIF 連接),音頻板將在軟件中初始化音頻表(音量計(jì))。
-
如果 S/PDIF PLL 時(shí)鐘失去頻率鎖定,音頻板將重置音頻表并等待新的連接。
-
如果通過 S/PDIF 接收到音頻采樣,采樣將被送入音頻表管道。
-
每輸入 M 個(gè)采樣點(diǎn),音頻電路板就會(huì)向電子管電路板發(fā)送新值。M 的計(jì)算是為了以 240Hz 的頻率更新電子管(或者在電子管數(shù)量非常多的情況下盡可能快)。
-
音頻板的速度非常快:
-
16 個(gè)電子管在 24bit/96kHz 的頻率下沒有問題
-
16 個(gè)電子管的更新頻率大于 400Hz,端到端延遲約為 3ms。默認(rèn)運(yùn)行頻率為 240Hz
外殼
舊款霓虹管時(shí)鐘遭棄用的核心敗筆在于丑陋的外殼。為避免重蹈覆轍,本次耗時(shí)月余打造激光切割亞克力外殼。
我在網(wǎng)上閱讀了許多文章,了解制作一個(gè)可用的亞克力產(chǎn)品外殼所面臨的挑戰(zhàn),最終我選擇了基于以下原則的設(shè)計(jì):頂板/背板/前面板/底板通過精密卡槽實(shí)現(xiàn)自校準(zhǔn)裝配,側(cè)板內(nèi)置彈簧卡扣確保整體結(jié)構(gòu)穩(wěn)固,驅(qū)動(dòng)板通過M2.5標(biāo)準(zhǔn)螺柱固定于殼體內(nèi)部。
從設(shè)計(jì)中可以得到一些啟示:
-
參數(shù)化設(shè)計(jì)非常有價(jià)值。手工進(jìn)行一次性設(shè)計(jì)很有誘惑力,但實(shí)際情況是,你幾乎肯定需要多次調(diào)整設(shè)計(jì)參數(shù)。進(jìn)行參數(shù)化 CAD 設(shè)計(jì)(無論是通過編程還是使用某些基于約束的工具)是值得的。
-
很難找到好的亞克力切割服務(wù)。我嘗試過很多流行的在線亞克力切割服務(wù),幾乎所有的服務(wù)都存在一些嚴(yán)重的問題(軟件問題、荒唐的運(yùn)費(fèi)/時(shí)間等)。最后我找到了一家當(dāng)?shù)氐男〉辏m然我必須用谷歌翻譯給他們發(fā)郵件,但他們的工作還不錯(cuò)。
-
應(yīng)該在設(shè)計(jì)參數(shù)中加入公差。如果你想讓復(fù)雜的零件緊密配合,就需要在制造過程中縮小可接受的誤差范圍。我的最終外殼設(shè)計(jì)只是勉強(qiáng)擠在一起(這正是我想要的,否則外殼就會(huì)松動(dòng))。
在殼體設(shè)計(jì)階段,我嘗試了多款CAD工具,最終參數(shù)化設(shè)計(jì)(parametric design)方案脫穎而出——這種設(shè)計(jì)方法允許快速調(diào)整參數(shù)而無需重構(gòu)整體結(jié)構(gòu)。
我試用過的最喜歡的 CAD 工具名為 SolveSpace,它是一款非常簡約的 3D CAD 工具,以約束求解引擎為基礎(chǔ)。然而,由于約束求解器在重復(fù)組方面存在一些明顯的限制,我無法使用該工具快速實(shí)現(xiàn)我的設(shè)計(jì)。
最后,我在 FreeCAD 中使用了 Python 腳本。FreeCAD 也有一個(gè)約束求解引擎,但我發(fā)現(xiàn)它使用起來很復(fù)雜(在重復(fù)群組上也有類似的問題),因此我只使用 Python API 來生成線、曲面和擠壓。FreeCAD 的 Python API 感覺有些笨拙,但還是完成了工作。
效果預(yù)覽
將電路板的 3D 模型從 KiCad 導(dǎo)出到 FreeCAD,以確保所有部件都能很好地組合在一起。當(dāng)我剛開始了解我想要的外殼外觀時(shí),我會(huì)將 FreeCAD 導(dǎo)出到 Blender,這樣我就可以渲染預(yù)覽視頻,了解整個(gè)外殼是如何組合在一起的。這就是其中一段視頻,當(dāng)時(shí)我正在考慮將控制板和輝光管放在同一個(gè)盒子里:
我使用 STEP 文件從 KiCAD 導(dǎo)出到 FreeCAD,并使用 Collada (.dae) 從 FreeCAD 導(dǎo)出到 blender。為了渲染如上圖所示的光線追蹤預(yù)覽效果,我要么讓 Blender 在我的電腦上運(yùn)行 5 個(gè)小時(shí),要么啟動(dòng)幾個(gè) EC2 GPU 實(shí)例,然后將工作量分?jǐn)偨o它們。Blender 有一個(gè)功能齊全的 Python API(雖然很笨拙),很適合做這樣的事情。
本文轉(zhuǎn)載自https://yager.io/vumeter/vu.html,以經(jīng)過翻譯及校對(duì)。
-
指示器
+關(guān)注
關(guān)注
0文章
254瀏覽量
38651 -
輝光管
+關(guān)注
關(guān)注
3文章
13瀏覽量
5389
發(fā)布評(píng)論請(qǐng)先 登錄
超級(jí)電容在故障指示器中的作用有哪些?

Linux simple-audio-card缺少音量控制怎么解決?
淺談架空暫態(tài)特征型遠(yuǎn)傳故障指示器
線路故障指示器為什么變成紅色
線路故障指示器如何復(fù)位
線路故障指示器工作原理是什么
線路故障指示器怎么判斷故障點(diǎn)
在現(xiàn)代系統(tǒng)設(shè)計(jì)中啟用LED和LCD指示器應(yīng)用簡介

使用電源正常狀態(tài)指示器實(shí)現(xiàn)輸出放電功能

7月全志芯片開源項(xiàng)目分享合輯
L60系列0.230英寸(5.9毫米)防水面板安裝指示器
利用Arduino的數(shù)字水位指示器電路設(shè)計(jì)

VL53L8CX TOF開發(fā)(4)----運(yùn)動(dòng)指示器

內(nèi)置超級(jí)電容模塊的故障指示器有哪些特性?

評(píng)論