はじめに
MATLAB Support Package for Raspberry Pi Hardwareを使用すると、MATLABを実行しているコンピューターとRaspberry Piとの通信を簡単に行うことができます。
例えば、Raspberry Piボード上のGPIOやI2CをMATLABから制御することが可能です。
本記事では、MATLAB Support Package for Raspberry Pi Hardwareの使用例として、MATLABからロボットカー学習キットのSunFounder(Smart Video Car Kit V2.0 for Raspberry Pi)を動かしてみた例について紹介します。
想定読者
MATLAB初級者から中級者レベルで、MATLAB Support Package for Raspberry Pi HardwareでのI2C通信処理に興味のある方を想定しています。
開発環境
OS: Windows 10
MATLAB: R2021a Update 5 (MATLAB Support Package for Raspberry Pi Hardware ver.21.1.4)
Board: Raspberry Pi 3B (MathWorks Raspbian Image(mathworks_raspbian_R21.1.0))
Network Settings: Connect to wireless network
Controlled Car: SunFounder (Smart Video Car Kit V2.0 for Raspberry Pi)
MATLABコード
MATLABとRaspberry Piの接続
最初にMATLABを実行しているコンピューターとRaspberry Piとの接続を確立します。
% Raspberry Piと接続します。
% パラメーターは各自の環境に合わせて修正してください。
ip_address = '192.168.2.63';
user_name = 'pi';
password = 'raspberry';
mypi = raspi(ip_address, user_name, password);
Raspberry Pi上の I2C デバイスに接続
SunFounderは、I2C接続のPWM/サーボドライバーであるPCA9685を使用して、駆動用モーターとステアリング用モーターを制御します。
Raspberry Pi 上の I2C デバイスに接続するため、下記のコードを実行します。
% Raspberry Pi 上の I2C デバイスに接続します。
% パラメーターは各自の環境に合わせて修正してください。
bus = 'i2c-1';
i2c_address = '0x40';
PCA9685 = i2cdev(mypi, bus, i2c_address);
PCA9685の設定
PCA9685の初期設定
PCA9685 の初期設定を行います。
% PCA9685 のレジスタ名とレジスタ番号を紐付け
MODE1_REGISTER = hex2dec('0x00');
MODE2_REGISTER = hex2dec('0x01');
ALL_LED_ON_L_REG = hex2dec('0xFA');
ALL_LED_ON_H_REG = hex2dec('0xFB');
ALL_LED_OFF_L_REG = hex2dec('0xFC');
ALL_LED_OFF_H_REG = hex2dec('0xFD');
OUTDRV = hex2dec('0x04');
ALLCALL = hex2dec('0x01');
% ALL_LED full ON/OFF をクリアします。
PCA9685.writeRegister(ALL_LED_ON_L_REG, 0, 'uint8');
PCA9685.writeRegister(ALL_LED_ON_H_REG, 0, 'uint8');
PCA9685.writeRegister(ALL_LED_OFF_L_REG, 0, 'uint8');
PCA9685.writeRegister(ALL_LED_OFF_H_REG, 0, 'uint8');
% MODE1とMODE2を再設定します。
PCA9685.writeRegister(MODE2_REGISTER, OUTDRV, 'uint8');
PCA9685.writeRegister(MODE1_REGISTER, ALLCALL, 'uint8');
pause(0.05);
PCA9685のプレスケール値(周波数)設定
PWM出力計算で使用するプレスケール値を計算します。
osc_clock = 25e6; % [Hz]
update_rate = 60; % [Hz]
resolution = 4096;
prescale_value = uint8(round(osc_clock / (resolution * update_rate)) - 1);
プレスケール値設定を可能にするためには、MODE1レジスタのSLEEPビットを「1」に設定します。
old_mode = PCA9685.readRegister(MODE1_REGISTER, 'uint8');
new_mode = bitor(bitand(old_mode, hex2dec('0x7F')), hex2dec('0x10'));
PCA9685.writeRegister(MODE1_REGISTER, new_mode); % Sleepモード設定
PRE_SCALEレジスタに、先程計算したプレスケール値を書き込み、Sleepモードを解除します。
PRE_SCALE_REGISTER = hex2dec('0xFE');
RESTART = hex2dec('0x80');
PCA9685.writeRegister(PRE_SCALE_REGISTER, prescale_value); % プレスケール値の書き込み
PCA9685.writeRegister(MODE1_REGISTER, old_mode); % Sleepモード解除
pause(0.05); % wait for oscillator
PCA9685.writeRegister(MODE1_REGISTER, bitor(old_mode, RESTART));
ステアリング制御サーボモーター用PWM出力チャネルのレジスタ定義
ステアリング用サーボモーター用 PWM 出力には CH0 を利用しているため、PCA9685 で使用するレジスタは LED0_ON_L から LED0_OFF_H になります。
LED0_ON_L_REG = hex2dec('0x06');
LED0_ON_H_REG = hex2dec('0x07');
LED0_OFF_L_REG = hex2dec('0x08');
LED0_OFF_H_REG = hex2dec('0x09');
ステアリング制御の確認
初期位置への移動
ステアリングを初期位置に移動させるためのPWM出力値(デューティ)を計算し、PCA9685で出力します。
duty_cycle_max = 2.25; % [ms]
duty_cycle_min = 0.75; % [ms]
max_travel = 140; % [deg]
pwm_period = 1 / update_rate * 1000; % [ms]
pwm_max = duty_cycle_max / pwm_period * resolution;
pwm_min = duty_cycle_min / pwm_period * resolution;
control_angle = 120; % [deg]
pwm_duty = uint16(pwm_min + (pwm_max - pwm_min) / max_travel * control_angle); % 角度をPWM出力値に変換
pause(1);
PCA9685.writeRegister(LED0_ON_L_REG, 0, 'uint8'); % Delay time = 0%
PCA9685.writeRegister(LED0_ON_H_REG, 0, 'uint8'); % Delay time = 0%
PCA9685.writeRegister(LED0_OFF_L_REG, bitand(pwm_duty, uint16(hex2dec('0xFF'))), 'uint8');
PCA9685.writeRegister(LED0_OFF_H_REG, bitshift(pwm_duty, -8), 'uint8'); % 右8ビットシフト
左右に操舵
PCA9685 を制御して、ステアリングを左右に動かしてみます。
steer_offset = -30;
for steer_angle = (120:-5:60) + steer_offset
pwm_duty = uint16(pwm_min + (pwm_max - pwm_min) / max_travel * steer_angle); % 角度をPWM出力値に変換
PCA9685.writeRegister(LED0_ON_L_REG, 0, 'uint8'); % Delay time = 0%
PCA9685.writeRegister(LED0_ON_H_REG, 0, 'uint8'); % Delay time = 0%
PCA9685.writeRegister(LED0_OFF_L_REG, bitand(pwm_duty, uint16(hex2dec('0xFF'))), 'uint8');
PCA9685.writeRegister(LED0_OFF_H_REG, bitshift(pwm_duty, -8), 'uint8');
pause(0.1);
end
pause(1);
for steer_angle = (60:5:120) + steer_offset
pwm_duty = uint16(pwm_min + (pwm_max - pwm_min) / max_travel * steer_angle); % 角度をPWM出力値に変換
PCA9685.writeRegister(LED0_ON_L_REG, 0, 'uint8'); % Delay time = 0%
PCA9685.writeRegister(LED0_ON_H_REG, 0, 'uint8'); % Delay time = 0%
PCA9685.writeRegister(LED0_OFF_L_REG, bitand(pwm_duty, uint16(hex2dec('0xFF'))), 'uint8');
PCA9685.writeRegister(LED0_OFF_H_REG, bitshift(pwm_duty, -8), 'uint8');
pause(0.1);
end
pause(1);
for steer_angle = (120:-5:90) + steer_offset
pwm_duty = uint16(pwm_min + (pwm_max - pwm_min) / max_travel * steer_angle); % 角度をPWM出力値に変換
PCA9685.writeRegister(LED0_ON_L_REG, 0, 'uint8'); % Delay time = 0%
PCA9685.writeRegister(LED0_ON_H_REG, 0, 'uint8'); % Delay time = 0%
PCA9685.writeRegister(LED0_OFF_L_REG, bitand(pwm_duty, uint16(hex2dec('0xFF'))), 'uint8');
PCA9685.writeRegister(LED0_OFF_H_REG, bitshift(pwm_duty, -8), 'uint8');
pause(0.1);
end
pause(1);
前後進制御の確認
駆動制御DCモーター用 PWM 出力は、PCA9685 PWMドライバー基板の CH4、5 を使用しています。
駆動用DCモーター制御用PWM出力レジスタの設定
PCA9685 PWMドライバー基板の CH4、5 に相当する PCA9685 レジスタは、LED4_ON_L から LED5_OFF_H になります。
LED4_ON_L_REG = hex2dec('0x16');
LED4_ON_H_REG = hex2dec('0x17');
LED4_OFF_L_REG = hex2dec('0x18');
LED4_OFF_H_REG = hex2dec('0x19');
LED5_ON_L_REG = hex2dec('0x1A');
LED5_ON_H_REG = hex2dec('0x1B');
LED5_OFF_L_REG = hex2dec('0x1C');
LED5_OFF_H_REG = hex2dec('0x1D');
DCモータードライバーの回転方向設定 Input のチャネル設定
DCモータードライバーの回転方向設定は、Raspberry Pi の "GPIO 17" と "GPIO 27" を使用しています。
MA = 17;
MB = 27;
mypi.configurePin(MA, 'DigitalOutput');
mypi.configurePin(MB, 'DigitalOutput');
前進駆動の確認
DCモーター制御基板に制御信号を入力して、左右駆動輪の回転制御を確認します。
まず前進駆動から確認します。
% 駆動方向設定
mypi.writeDigitalPin(MA, false);
mypi.writeDigitalPin(MB, false);
% 加速
for speed = 5:10:95
pwm_duty = uint16(speed / 100 * resolution);
% 右後輪の駆動
PCA9685.writeRegister(LED4_ON_L_REG, 0, 'uint8');
PCA9685.writeRegister(LED4_ON_H_REG, 0, 'uint8');
PCA9685.writeRegister(LED4_OFF_L_REG, bitand(pwm_duty, uint16(hex2dec('0xFF'))), 'uint8');
PCA9685.writeRegister(LED4_OFF_H_REG, bitshift(pwm_duty, -8), 'uint8');
% 左後輪の駆動
PCA9685.writeRegister(LED5_ON_L_REG, 0, 'uint8');
PCA9685.writeRegister(LED5_ON_H_REG, 0, 'uint8');
PCA9685.writeRegister(LED5_OFF_L_REG, bitand(pwm_duty, uint16(hex2dec('0xFF'))), 'uint8');
PCA9685.writeRegister(LED5_OFF_H_REG, bitshift(pwm_duty, -8), 'uint8');
pause(1);
end
% 減速
for speed = 95:-10:5
pwm_duty = uint16(speed / 100 * resolution);
% 右後輪の駆動
PCA9685.writeRegister(LED4_ON_L_REG, 0, 'uint8');
PCA9685.writeRegister(LED4_ON_H_REG, 0, 'uint8');
PCA9685.writeRegister(LED4_OFF_L_REG, bitand(pwm_duty, uint16(hex2dec('0xFF'))), 'uint8');
PCA9685.writeRegister(LED4_OFF_H_REG, bitshift(pwm_duty, -8), 'uint8');
% 左後輪の駆動
PCA9685.writeRegister(LED5_ON_L_REG, 0, 'uint8');
PCA9685.writeRegister(LED5_ON_H_REG, 0, 'uint8');
PCA9685.writeRegister(LED5_OFF_L_REG, bitand(pwm_duty, uint16(hex2dec('0xFF'))), 'uint8');
PCA9685.writeRegister(LED5_OFF_H_REG, bitshift(pwm_duty, -8), 'uint8');
pause(1);
end
% DCモーターの回転を停止します。
PCA9685.writeRegister(LED4_ON_L_REG, 0, 'uint8');
PCA9685.writeRegister(LED4_ON_H_REG, 0, 'uint8');
PCA9685.writeRegister(LED4_OFF_L_REG, 0, 'uint8');
PCA9685.writeRegister(LED4_OFF_H_REG, 0, 'uint8');
PCA9685.writeRegister(LED5_ON_L_REG, 0, 'uint8');
PCA9685.writeRegister(LED5_ON_H_REG, 0, 'uint8');
PCA9685.writeRegister(LED5_OFF_L_REG, 0, 'uint8');
PCA9685.writeRegister(LED5_OFF_H_REG, 0, 'uint8');
後進駆動の確認
後進駆動の確認をします。
% 駆動方向設定
mypi.writeDigitalPin(MA, true);
mypi.writeDigitalPin(MB, true);
% 加速
for speed = 5:10:95
pwm_duty = uint16(speed / 100 * resolution);
% 右後輪の駆動
PCA9685.writeRegister(LED4_ON_L_REG, 0, 'uint8');
PCA9685.writeRegister(LED4_ON_H_REG, 0, 'uint8');
PCA9685.writeRegister(LED4_OFF_L_REG, bitand(pwm_duty, uint16(hex2dec('0xFF'))), 'uint8');
PCA9685.writeRegister(LED4_OFF_H_REG, bitshift(pwm_duty, -8), 'uint8');
% 左後輪の駆動
PCA9685.writeRegister(LED5_ON_L_REG, 0, 'uint8');
PCA9685.writeRegister(LED5_ON_H_REG, 0, 'uint8');
PCA9685.writeRegister(LED5_OFF_L_REG, bitand(pwm_duty, uint16(hex2dec('0xFF'))), 'uint8');
PCA9685.writeRegister(LED5_OFF_H_REG, bitshift(pwm_duty, -8), 'uint8');
pause(1);
end
% 減速
for speed = 95:-10:5
pwm_duty = uint16(speed / 100 * resolution);
% 右後輪の駆動
PCA9685.writeRegister(LED4_ON_L_REG, 0, 'uint8');
PCA9685.writeRegister(LED4_ON_H_REG, 0, 'uint8');
PCA9685.writeRegister(LED4_OFF_L_REG, bitand(pwm_duty, uint16(hex2dec('0xFF'))), 'uint8');
PCA9685.writeRegister(LED4_OFF_H_REG, bitshift(pwm_duty, -8), 'uint8');
% 左後輪の駆動
PCA9685.writeRegister(LED5_ON_L_REG, 0, 'uint8');
PCA9685.writeRegister(LED5_ON_H_REG, 0, 'uint8');
PCA9685.writeRegister(LED5_OFF_L_REG, bitand(pwm_duty, uint16(hex2dec('0xFF'))), 'uint8');
PCA9685.writeRegister(LED5_OFF_H_REG, bitshift(pwm_duty, -8), 'uint8');
pause(1);
end
% 停止
PCA9685.writeRegister(LED4_ON_L_REG, 0, 'uint8');
PCA9685.writeRegister(LED4_ON_H_REG, 0, 'uint8');
PCA9685.writeRegister(LED4_OFF_L_REG, 0, 'uint8');
PCA9685.writeRegister(LED4_OFF_H_REG, 0, 'uint8');
PCA9685.writeRegister(LED5_ON_L_REG, 0, 'uint8');
PCA9685.writeRegister(LED5_ON_H_REG, 0, 'uint8');
PCA9685.writeRegister(LED5_OFF_L_REG, 0, 'uint8');
PCA9685.writeRegister(LED5_OFF_H_REG, 0, 'uint8');
終了処理
動作確認を終了します。
clear PCA9685 mypi
実行例
まとめ
MATLAB Support Package for Raspberry Pi Hardwareを使用して、MATLABからSunFounder(Smart Video Car Kit V2.0 for Raspberry Pi)を動かす例を紹介しました。MATLABからRaspberry Piを使用する際の参考になれば幸いです。
今後、Simulinkでの実行例についても紹介する予定です。