譯者 | 朱先忠
審校 | 重樓
引言
人形機(jī)器人,是指外形和動(dòng)作都與人體相似的機(jī)器,旨在與人類協(xié)同工作并與我們的工具進(jìn)行交互。雖然這項(xiàng)技術(shù)仍處于發(fā)展初期,但據(jù)預(yù)測,到2050年,人形機(jī)器人的數(shù)量將達(dá)到數(shù)十億。目前,最先進(jìn)的原型機(jī)包括:1XTech公司的NEO、特斯拉公司的Optimus、波士頓動(dòng)力公司的Atlas以及中國宇創(chuàng)科技的G1。
機(jī)器人執(zhí)行任務(wù)的方式有兩種:手動(dòng)控制(即預(yù)先編寫程序控制其行為)和人工智能(即通過反復(fù)嘗試學(xué)習(xí)如何完成任務(wù))。具體來說,強(qiáng)化學(xué)習(xí)使機(jī)器人能夠通過試錯(cuò)法學(xué)習(xí)達(dá)成目標(biāo)的最佳行動(dòng)方案,從而在沒有預(yù)先設(shè)定計(jì)劃的情況下,通過學(xué)習(xí)獎(jiǎng)勵(lì)和懲罰機(jī)制來適應(yīng)不斷變化的環(huán)境。
實(shí)際上,讓一個(gè)真正的機(jī)器人學(xué)習(xí)如何執(zhí)行任務(wù)成本極其高昂。因此,目前最先進(jìn)的方法是在仿真環(huán)境中進(jìn)行學(xué)習(xí),因?yàn)榉抡姝h(huán)境的數(shù)據(jù)生成速度快、成本低,然后將學(xué)習(xí)到的知識(shí)遷移到真正的機(jī)器人上(“仿真到真實(shí)”/“仿真優(yōu)先”方法)。這使得在仿真環(huán)境中并行訓(xùn)練多個(gè)模型成為可能。
當(dāng)前,市面上最常用的3D物理模擬器包括:PyBullet(入門級(jí))、Webots(中級(jí))、MuJoCo(高級(jí))和Gazebo(專業(yè)級(jí))。你可以將它們作為獨(dú)立軟件使用,也可以通過Gym庫來使用。Gym是由OpenAI開發(fā)的強(qiáng)化學(xué)習(xí)算法庫,它基于不同的物理引擎構(gòu)建。
在本教程中,我將展示如何構(gòu)建一個(gè)具有人工智能的人形機(jī)器人的3D仿真模型。我將提供一些實(shí)用的Python代碼,這些代碼可以輕松應(yīng)用于其他類似場景(只需復(fù)制、粘貼、運(yùn)行),并逐行講解代碼,以便你能夠復(fù)現(xiàn)此示例(文章末尾附有示例工程的完整的源代碼的鏈接)。
設(shè)置
環(huán)境是一個(gè)模擬空間,智能體可以在其中進(jìn)行交互并學(xué)習(xí)執(zhí)行任務(wù)。它具有明確的觀察空間(智能體接收的信息)和行動(dòng)空間(可能采取的行動(dòng)集合)。
我將使用Gym(pip install gymnasium)加載使用MuJoCo(多關(guān)節(jié)動(dòng)力學(xué)與接觸)創(chuàng)建的默認(rèn)環(huán)境之一。
pip install mujoco
import gymnasium as gym
env = gym.make("Humanoid-v4", render_mode="human")
obs, info = env.reset()
env.render()
該智能體是一個(gè)能夠像人類一樣移動(dòng)的3D雙足機(jī)器人。它有12個(gè)連桿(剛性部件)和17個(gè)關(guān)節(jié)(柔性部件)。你可以在這里查看完整描述。
在開始新的模擬之前,必須使用`setenv`命令重置環(huán)境obs, info = env.reset()。該命令會(huì)返回有關(guān)智能體初始狀態(tài)的信息。通常,info還包含有關(guān)機(jī)器人的額外信息。

雖然這個(gè)obs是智能體(例如通過傳感器)所看到的,但人工智能模型需要處理這些觀察結(jié)果才能決定采取什么行動(dòng)。

通常,所有Gym環(huán)境都具有相同的結(jié)構(gòu)。首先要檢查的是動(dòng)作空間,即所有可能動(dòng)作的集合。對于人形機(jī)器人模擬,一個(gè)動(dòng)作代表施加在其17個(gè)關(guān)節(jié)之一上的力(范圍在-0.4到+0.4之間,以指示推力的方向)。
env.action_space
env.action_space.sample()
模擬至少應(yīng)涵蓋一個(gè)完整回合,即智能體與環(huán)境交互的完整過程,從開始到結(jié)束。每個(gè)回合都是一個(gè)循環(huán):reset() -> step() -> render()。讓我們舉一個(gè)例子,讓類人機(jī)器人做隨機(jī)動(dòng)作,而不是人工智能。
import time
env = gym.make("Humanoid-v4", render_mode="human")
obs, info = env.reset()
reset = False #如果人形機(jī)器人摔倒或這一部分動(dòng)作結(jié)束,則重置
episode = 1
total_reward, step = 0, 0
for _ in range(240):
## 動(dòng)作
step += 1
action = env.action_space.sample() #隨機(jī)動(dòng)作
obs, reward, terminated, truncated, info = env.step(action)
## 獎(jiǎng)勵(lì)
total_reward += reward
## 渲染
env.render() #render physics step (CPU speed = 0.1 seconds)
time.sleep(1/240) #減慢到實(shí)時(shí)情況下 (240 steps × 1/240 second sleep = 1 second)
if (step == 1) or (step % 100 == 0): #打印第一步和每100步
print(f"EPISODE {episode} - Step:{step}, Reward:{reward:.1f}, Total:{total_reward:.1f}")
## 重置
if reset:
if terminated or truncated: #打印最后一步
print(f"EPISODE {episode} - Step:{step}, Reward:{reward:.1f}, Total:{total_reward:.1f}")
obs, info = env.reset()
episode += 1
total_reward, step = 0, 0
print("------------------------------------------")
env.close()

隨著游戲進(jìn)程的推進(jìn),機(jī)器人不斷移動(dòng),我們會(huì)獲得獎(jiǎng)勵(lì)。在這種情況下,如果機(jī)器人保持站立或向前移動(dòng),則獲得正獎(jiǎng)勵(lì);如果機(jī)器人跌倒并觸地,則受到負(fù)懲罰。獎(jiǎng)勵(lì)是人工智能中最重要的概念,因?yàn)樗x了目標(biāo)。它是每次動(dòng)作后我們從環(huán)境獲得的反饋信號(hào),指示該動(dòng)作是否有效。因此,我們可以利用獎(jiǎng)勵(lì),通過強(qiáng)化學(xué)習(xí)來優(yōu)化機(jī)器人的決策。
強(qiáng)化學(xué)習(xí)
在模擬的每一步,智能體都會(huì)觀察當(dāng)前情況(即其在環(huán)境中的位置),決定采取什么行動(dòng)(即移動(dòng)某個(gè)關(guān)節(jié)),并收到正面或負(fù)面的反饋(獎(jiǎng)勵(lì)或懲罰)。這個(gè)循環(huán)不斷重復(fù),直到模擬結(jié)束。強(qiáng)化學(xué)習(xí)是一種機(jī)器學(xué)習(xí)方法,它通過反復(fù)試錯(cuò)使智能體最大化獎(jiǎng)勵(lì)。因此,如果成功,機(jī)器人就能知道最佳行動(dòng)方案是什么。
從數(shù)學(xué)角度來看,強(qiáng)化學(xué)習(xí)基于馬爾可夫決策過程;其中,未來僅取決于當(dāng)前情況,而與過去無關(guān)。簡單來說,智能體無需記憶之前的步驟即可決定下一步行動(dòng)。例如,機(jī)器人只需知道其當(dāng)前位置和速度即可選擇下一步移動(dòng),無需記住它是如何到達(dá)那里的。
強(qiáng)化學(xué)習(xí)的核心在于最大化獎(jiǎng)勵(lì)。因此,構(gòu)建模擬系統(tǒng)的關(guān)鍵在于設(shè)計(jì)一個(gè)能夠真正反映預(yù)期結(jié)果的獎(jiǎng)勵(lì)函數(shù)(這里的目標(biāo)是避免失?。?。最基本的強(qiáng)化學(xué)習(xí)算法會(huì)在獲得正獎(jiǎng)勵(lì)后更新首選動(dòng)作列表。更新的速度就是學(xué)習(xí)率:如果學(xué)習(xí)率過高,智能體會(huì)過度修正;如果學(xué)習(xí)率過低,智能體則會(huì)不斷犯同樣的錯(cuò)誤,學(xué)習(xí)速度極其緩慢。
首選行動(dòng)的更新也受到探索率的影響,探索率是隨機(jī)選擇的頻率,本質(zhì)上反映了人工智能的好奇心。通常,探索率在初期(智能體一無所知時(shí))相對較高,隨著機(jī)器人不斷積累知識(shí),探索率會(huì)逐漸下降。
import gymnasium as gym
import time
import numpy as np
env = gym.make("Humanoid-v4", render_mode="human")
obs, info = env.reset()
reset = True #如果人形機(jī)器人摔倒或這一部分動(dòng)作結(jié)束,則重置
episode = 1
total_reward, step = 0, 0
exploration_rate = 0.5
preferred_action = np.zeros(env.action_space.shape) #知識(shí)隨著經(jīng)驗(yàn)而更新
for _ in range(1000):
## 動(dòng)作
step += 1
exploration = np.random.normal(loc=0, scale=exploration_rate, size=env.action_space.shape) #add random noise
action = np.clip(a=preferred_action+exploration, a_min=-1, a_max=1)
obs, reward, terminated, truncated, info = env.step(action)
## 獎(jiǎng)勵(lì)
total_reward += reward
if reward > 0:
preferred_action += (action-preferred_action)*0.05 #學(xué)習(xí)率
exploration_rate = max(0.05, exploration_rate*0.99) #min_exploration=0.05, decay_exploration=0.99
## 渲染
env.render()
time.sleep(1/240)
if (step == 1) or (step % 100 == 0):
print(f"EPISODE {episode} - Step:{step}, Reward:{reward:.1f}, Total:{total_reward:.1f}")
## 重置
if reset:
if terminated or truncated:
print(f"EPISODE {episode} - Step:{step}, Reward:{reward:.1f}, Total:{total_reward:.1f}")
obs, info = env.reset()
episode += 1
total_reward, step = 0, 0
print("------------------------------------------")
env.close()

顯然,對于像人形機(jī)器人這樣復(fù)雜的環(huán)境來說,這太簡單了;所以,即使智能體更新了首選動(dòng)作,它仍然會(huì)不斷跌倒。
深度強(qiáng)化學(xué)習(xí)
當(dāng)行為與獎(jiǎng)勵(lì)之間的關(guān)系是非線性的時(shí),就需要神經(jīng)網(wǎng)絡(luò)。深度強(qiáng)化學(xué)習(xí)能夠處理高維輸入,并利用深度神經(jīng)網(wǎng)絡(luò)的強(qiáng)大功能來估計(jì)行為的預(yù)期未來獎(jiǎng)勵(lì)。
在Python中,使用深度強(qiáng)化學(xué)習(xí)算法最簡單的方法是通過StableBaseline,它收集了最知名的模型,這些模型已經(jīng)預(yù)先實(shí)現(xiàn),可以直接使用。請注意,StableBaseline分為兩個(gè)版本:StableBaseline(用TensorFlow編寫)和StableBaselines3(用PyTorch編寫)。目前,大家都在使用后者。
pip install torch
pip install stable-baselines3近端策略優(yōu)化(PPO)是深度強(qiáng)化學(xué)習(xí)中最常用的算法之一,因?yàn)樗唵吻曳€(wěn)定。PPO的目標(biāo)是在保持策略穩(wěn)定增長的前提下,通過對策略進(jìn)行微小的更新來最大化預(yù)期總獎(jiǎng)勵(lì)。
我將使用StableBaseline在Gym Humanoid環(huán)境下訓(xùn)練一個(gè)PPO模型。以下幾點(diǎn)需要注意:
- 我們不需要對環(huán)境進(jìn)行圖形渲染,因此訓(xùn)練可以加快速度。
- 必須將Gym環(huán)境封裝起來DummyVecEnv,使其與StableBaseline矢量化格式兼容。
- 關(guān)于神經(jīng)網(wǎng)絡(luò)模型,PPO使用多層感知器(MlpPolicy)處理數(shù)值輸入,使用卷積神經(jīng)網(wǎng)絡(luò)(CnnPolicy)處理圖像,使用組合模型(MultiInputPolicy)處理混合類型的觀測值。
- 由于我沒有渲染人形模型,我發(fā)現(xiàn)使用TensorBoard(一個(gè)用于實(shí)時(shí)可視化統(tǒng)計(jì)數(shù)據(jù)的工具包pip install tensorboard)來查看訓(xùn)練進(jìn)度非常有用。我創(chuàng)建了一個(gè)名為“l(fā)ogs”的文件夾,然后就可以tensorboard --logdir=logs/在終端運(yùn)行命令來在本地查看儀表盤http://localhost:6006/了。
from stable_baselines3 import PPO
from stable_baselines3.common.vec_env import DummyVecEnv
## 環(huán)境
env = gym.make("Humanoid-v4") #無需加速渲染
env = DummyVecEnv([lambda:env])
## 訓(xùn)練
print("Training START")
model = PPO(policy="MlpPolicy", env=env, verbose=0,
learning_rate=0.005, ent_coef=0.005, #探索
tensorboard_log="logs/") #>tensorboard --logdir=logs/
model.learn(total_timesteps=3_000_000, #1h
tb_log_name="model_humanoid", log_interval=10)
print("Training DONE")
## 保存
model.save("model_humanoid")
訓(xùn)練完成后,我們可以加載新模型并在渲染環(huán)境中進(jìn)行測試。此時(shí),智能體將不再更新首選動(dòng)作,而是使用訓(xùn)練好的模型,根據(jù)當(dāng)前狀態(tài)預(yù)測下一個(gè)最佳動(dòng)作。
env = gym.make("Humanoid-v4", render_mode="human")
model = PPO.load(path="model_humanoid", env=env)
obs, info = env.reset()
reset = False #如果人形機(jī)器人摔倒或這一部分動(dòng)作結(jié)束;則重置
episode = 1
total_reward, step = 0, 0
for _ in range(1000):
## 動(dòng)作
step += 1
action, _ = model.predict(obs)
obs, reward, terminated, truncated, info = env.step(action)
## 獎(jiǎng)勵(lì)
total_reward += reward
## 渲染
env.render()
time.sleep(1/240)
if (step == 1) or (step % 100 == 0): #打印第一步和每100步
print(f"EPISODE {episode} - Step:{step}, Reward:{reward:.1f}, Total:{total_reward:.1f}")
## 重置
if reset:
if terminated or truncated: #打印最后一步
print(f"EPISODE {episode} - Step:{step}, Reward:{reward:.1f}, Total:{total_reward:.1f}")
obs, info = env.reset()
episode += 1
total_reward, step = 0, 0
print("------------------------------------------")
env.close()
請注意,本教程中我們從未專門編寫程序讓機(jī)器人保持站立。我們并沒有控制機(jī)器人;機(jī)器人只是對環(huán)境的獎(jiǎng)勵(lì)函數(shù)做出反應(yīng)。事實(shí)上,如果你訓(xùn)練強(qiáng)化學(xué)習(xí)模型更長時(shí)間(例如3000萬個(gè)時(shí)間步),你不僅會(huì)看到機(jī)器人完美地站立起來,還會(huì)看到它向前行走。因此,在訓(xùn)練人工智能智能體時(shí),3D世界的設(shè)計(jì)及其規(guī)則比構(gòu)建機(jī)器人本身更為重要。
結(jié)論
本文旨在介紹MuJoCo和Gym,以及如何創(chuàng)建用于機(jī)器人的3D仿真模型。我們使用人形機(jī)器人環(huán)境來學(xué)習(xí)強(qiáng)化學(xué)習(xí)的基礎(chǔ)知識(shí)。具體地說,我們訓(xùn)練了一個(gè)深度神經(jīng)網(wǎng)絡(luò)來教會(huì)機(jī)器人如何避免跌倒。后續(xù)還將推出使用更高級(jí)機(jī)器人的教程。
本文完整源代碼:GitHub
譯者介紹
朱先忠,51CTO社區(qū)編輯,51CTO專家博客、講師,濰坊一所高校計(jì)算機(jī)教師,自由編程界老兵一枚。































