## 簡介
我目前擁有一台可程式操控的機器人,並搭載了一個力與力矩感測器。這套系統能透過感測器數據進行後續的控制演算法,例如觸覺回饋(Haptic Feedback)或順應性控制(Admittance Control)。
最初的設計中,感測器數據讀取與機器人控制功能被寫在同一個程式內。然而,這樣的設計存在一個明顯的缺點:當感測器停止量測時,機器人也必須停止運行。這對於需要多元控制方式的機器人來說並不方便,畢竟感測器數據只是控制系統中的一部分。
因此,我決定將程式進行模組化,將感測器與機器人控制分開設計。為了讓兩個模組能夠高效地交換數據,我選擇了共享記憶體作為實現的方式。

**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!")
```
### 執行結果

> writer執行時不斷將數值寫入共享記憶體,直到使用者按下ESC時停止寫入

> 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)