こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

30~60秒に1回モーターを回したい

古いH8のマイコンが載った、(Beauto Chaserと呼ばれる)車型のロボットで30秒~60秒に1回ランダムにモーターを前後に回したいです。しかし、思うように動きません。

自分でC言語のコードを書いてHEWとFDTで読み込ませていますが、なぜかいつも32秒±1秒くらいまで待って動き始めます。しかも、プログラム上は前後の動きは30秒~60秒に1回しか無いはずなのに、8回とか10回とか繰り返してから、次の32秒±1秒のルーチンに入るようです。何がおかしいのでしょうか?

これが私のコードです:

#include<36064s.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include "vs-wrc003.h"

void main(void)
{
const BYTE MainCycle = 60;
Init((BYTE)MainCycle);//CPUの初期設定
Mtr_Run(0,0,0,0);//モータを停止
LED(2);//オレンジのLED点灯

//ループ
while(1) {//メインループ
if(getSW()) {//スイッチが押されたら
int r = 30000;
while(1) {
Mtr_Run(-64,64,0,0);//前に進む
Wait(500);
Mtr_Run(64,-64,0,0);//後ろに下がる
Wait(500);
Mtr_Run(0,0,0,0);//モータを停止

srand(r);
r = rand() % 30000;//0~29,999の乱数を生成する…はず
Wait(30000 + r);//30~60秒待つ…はず
}
}
}
}

…これを10~20秒に設定すると正確に動きます。
8回くらいテストしましたが、12~20秒の間でランダムに動きました。しかも、前後の動きは必ず1回だけです。

なぜ30~60秒にするとダメなのでしょうか?

H8/36064のデータシートは見つけました:
http://robot.tamagawa.ac.jp:8080/cyber/H8cpp/rescue/rjj09b0049_h836064.pdf

ただ、タイマーが複数あって、どこを見たらいいのか分かりません。404ページもあって上限○秒みたいなのは書いてないんですよね…見つけられてないだけかもしれませんが。
あれこれ試行錯誤する前に質問しようと思い、今、質問しています。
よろしくお願いします。

※<time.h>が使えないマイコンでの疑似乱数の生成方法については、次回質問します。今回は一応、動いているので気にしません。

投稿日時 - 2020-05-30 00:10:23

QNo.9754772

困ってます

質問者が選んだベストアンサー

>r = rand() % 30000;//0~29,999の乱数を生成する…はず
int rand(void) は、0からRAND_MAXの間の擬似乱数整数を生成します。
H8の<stdio.h>では、RAND_MAX=32767に定義されていますので
%30000の結果は 0~2767が2768~29999の2倍頻度あらわれることになり、30~32.8秒の頻度があがりますよ。
一方
>int r = 30000; (←もしかして、RAND_MAXと勘違いしてる?)
>srand(r);
で、ランダムシードを毎回30000に固定して設定しているので
そのシードから生成される乱数系列が、
質問文のような動作になる乱数系列だったのではないでしょうか。

投稿日時 - 2020-05-30 01:05:29

お礼

解決しました。
原因はWait()のカウンターの上限が32,767であることでした。
Wait(32767)を挟むと32.767ms待ってからモーターが回ります。
Wait(32768)を挟むと何も待たずにモーターが回り続けます。

それに気付いたのはNo.2さんの「RAND_MAX=32767」という回答でした。
そんなに小さいんですね…「RAND_MAX=2147483647=0x7fffffff」だと思って組んでました。

解決法としては、Wait()を分割しました。
最初に必ず30秒待ちたいので、Wait(30000);とし、
その後で乱数分待つことにしました。

>%30000の結果は 0~2767が2768~29999の2倍頻度あらわれることになり、30~32.8秒の頻度があがりますよ。

今回の主原因ではなかったですが、確かに偏りますね。
それを受け、今回は30までの乱数を生成し、それを1000倍することにしました。

>>int r = 30000; (←もしかして、RAND_MAXと勘違いしてる?)
>>srand(r);
>で、ランダムシードを毎回30000に固定して設定しているので

いえ、実はint r = 30000;はループの外にあり、ループの中ではrは
r = rand() % 30000;
で毎回更新されています。なので固定ではないですね、毎回同じパターンにはなりますけど。

参考までに完成したコードを貼っておきます:

void main(void)
{
//制御周期の設定[単位:Hz 範囲:30.0~]
const BYTE MainCycle = 60;
Init((BYTE)MainCycle);//CPUの初期設定
Mtr_Run(0,0,0,0);//モータを停止
LED(2);//オレンジのLED点灯

//ループ
while(1) {//メインループ
if(getSW()) {//スイッチが押されたら
int r = 123;
int waitTime = 0;
srand(r);
while(1) {
//Wait(32767);//待つ
//Wait(32768);//待たない
Mtr_Run(-64,64,0,0);//前に進む
Wait(500);
Mtr_Run(64,-64,0,0);//後ろに下がる
Wait(500);
Mtr_Run(0,0,0,0);//モータを停止

Wait(30000);//30秒待つ
r = rand() % 30;//0~29の乱数を生成する
Wait(r * 1000);//30秒~60秒待つ
}
}
}
}

…ありがとうございました。

投稿日時 - 2020-05-30 12:01:52

ANo.2

このQ&Aは役に立ちましたか?

0人が「このQ&Aが役に立った」と投票しています

回答(2)

ANo.1

>srand(r);
これって乱数の種を発生させる関数ですよね。
ならば、ループの中で毎回実行するのはまずいのでは?
プログラムの開始時に「1回だけ」実行するのが正しい、と思います。

投稿日時 - 2020-05-30 00:41:49

お礼

今回の主原因ではなかったですが、確かに一回で事足りましたね。
実は原因が疑似乱数にあるのかと思い、発生した乱数を種に次の乱数を発生させてみました。
まぁ、同じパターンにはなるんですが、解決の糸口にならないかと試行錯誤したまでです。
<time.h>が使えるといいんですけどね…。
ありがとうございました。

投稿日時 - 2020-05-30 12:06:07

あなたにオススメの質問