## 簡介 我目前擁有一台可程式操控的機器人,並搭載了一個力與力矩感測器。這套系統能透過感測器數據進行後續的控制演算法,例如觸覺回饋(Haptic Feedback)或順應性控制(Admittance Control)。 最初的設計中,感測器數據讀取與機器人控制功能被寫在同一個程式內。然而,這樣的設計存在一個明顯的缺點:當感測器停止量測時,機器人也必須停止運行。這對於需要多元控制方式的機器人來說並不方便,畢竟感測器數據只是控制系統中的一部分。 因此,我決定將程式進行模組化,將感測器與機器人控制分開設計。為了讓兩個模組能夠高效地交換數據,我選擇了共享記憶體作為實現的方式。 ![image](https://hackmd.io/_uploads/r10zyCTXyx.png) **Q: 如果我開多個thread並且將我要的數據存在global variable中,差別在哪裡?** 多個thread仍然在同一個process底下運作,當程式結束時所有thread也會同步停止。此外全域變數也只能在同一個process內使用,無法跨程式共享變數。 ## Shared Memory簡單範例 - Python程式說明 ### 功能說明 - 程式語言: Python - 作業系統: Windows 10 以下有writer和reader兩隻程式。Writer模擬感測器即時量測,並將數據寫入共享記憶體;而Reader則是模擬其他需要感測器資料的程式,從共享記憶體內取得數據。 ### Writer `inpurData`為一個7個元素的陣列,前六個entry拿來放感測器數據,最後一個entry拿來作為停止的digit,讓Reader知道感測器已經停止量測了。(資料這部分可以自己設計不同的功能)。 `data_buffer`是一個容器,他的記憶體位置直接映射(mapping)到shared memory裡面,因此只要改變`data_buffer`的變數值就可以寫入共享記憶體。 按下`ESC`可以停止寫入共享記憶體 ```Python= from multiprocessing import shared_memory import numpy as np import keyboard import time # Define the name of shared memory shm_name = "sensor_data" # Initialize the data container inputData = np.array([0, 0, 0, 0, 0, 0, 0], dtype=np.float32) # Create shared memory and specify the size as the memory size of inputData shm_data = shared_memory.SharedMemory(create=True, size=inputData.nbytes, name=shm_name) # Create a numpy ndarray using the buffer of shared memory data_buffer = np.ndarray(inputData.shape, dtype=np.float32, buffer=shm_data.buf) i = 0 while not keyboard.is_pressed("esc"): # Simluate the sensor data changing over time i = i + 1 inputData[0] = i # write the sensor data into shared memory data_buffer[:] = inputData[:] print("Data written to shared memory:", data_buffer) time.sleep(0.1) # The last digit represents the STOP condition for READER data_buffer[-1] = 1 # release the memory print("ESC is pressed!") shm_data.close() shm_data.unlink() print("Shared memory has been closed and unlink!") ``` ### Reader 這邊單純演示從共享記憶體撈取資料,當array最後一個entry為1時就代表感測器端沒有再繼續寫入數據,就停止程式,當然可以自行根據需求改變。 ```Python= from multiprocessing import shared_memory import numpy as np import time # The name of shared memory, which should be the same with WRITER shm_name = "sensor_data" # Number of array elements, same with inputData dataNum = 7 # Open the existed shared memmory shm_data = shared_memory.SharedMemory(name=shm_name) # Create a numpy ndarray using the buffer of shared memory data_buffer = np.ndarray((dataNum,), dtype=np.float32, buffer=shm_data.buf) while True: # Check the STOP condition written by WRITER if data_buffer[-1] == 1: break print("Data read from shared memory:", data_buffer) time.sleep(0.1) shm_data.close() shm_data.unlink() print("Shared memory has been closed and unlinked!") ``` ### 執行結果 ![image](https://hackmd.io/_uploads/SJJOdIhmkg.png) > writer執行時不斷將數值寫入共享記憶體,直到使用者按下ESC時停止寫入 ![image](https://hackmd.io/_uploads/Syhud8hm1x.png) > reader的確收到共享記憶體內的變數 ## C++實現共享記憶體 ```cpp= #pragma once #include <windows.h> #include <iostream> #include <tchar.h> class sharedMemory { public: sharedMemory(int size, LPCSTR buffer_name, LPCSTR mutex_name); ~sharedMemory(); void inputData(double* src); void outputData(double* dest); int bufferSize; HANDLE shmem = INVALID_HANDLE_VALUE; HANDLE mutex = INVALID_HANDLE_VALUE; double* buf; }; ``` ```cpp= #include "sharedMemory.h" sharedMemory::sharedMemory(int size, LPCSTR buffer_name, LPCSTR mutex_name) { bufferSize = size; mutex = ::CreateMutex(NULL, FALSE, TEXT(mutex_name)); shmem = ::CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, bufferSize, TEXT(buffer_name)); buf = (double*)::MapViewOfFile(shmem, FILE_MAP_ALL_ACCESS, 0, 0, size); if (shmem == NULL) { printf(TEXT("Could not create file mapping object (%d).\n"), GetLastError()); } if (buf == NULL) { printf(TEXT("Could not map view of file (%d).\n"), GetLastError()); CloseHandle(shmem); } } void sharedMemory::inputData(double* src) { WaitForSingleObject(mutex, INFINITE); memcpy(buf, src, bufferSize); ::ReleaseMutex(mutex); } void sharedMemory::outputData(double* dest) { WaitForSingleObject(mutex, INFINITE); memcpy(dest, buf, bufferSize); ::ReleaseMutex(mutex); } sharedMemory::~sharedMemory() { ::UnmapViewOfFile(buf); ::CloseHandle(shmem); ::ReleaseMutex(mutex); } ``` 跨程式語言在使用共享記憶體時要注意,C語言的double應該對應到Python中預設的float(即float64);而C裡面的float則是對到Python的float32 ## 參考資料 - [Python Docs multiprocessing.shared_memory -- 對於共享記憶體的跨行程直接存取](https://docs.python.org/zh-tw/3/library/multiprocessing.shared_memory.html) - [宇庭的筆記 - Shared memory](https://hackmd.io/@Lcum6qg_QzSSrLoHloMEXQ/Sk7Mhukg1l)