中山隼雄科学技術文化財団

研究者 各位
(本メールはこれまでに当財団の助成に採択された方にお送りしています)

平素より格別のご高配を賜り、厚く御礼申し上げます。
このたび、当財団では2026年度「調査研究」「研究助成」の募集を開始いたしましたので、ご案内申し上げます。

1. 研究助成の対象分野
以下の2分野を助成対象とします。
(1)助成研究A=ゲームの分野の研究
A-① 重点研究
A-② 基礎的・基盤的研究
(2)助成研究B=人間と遊びに関する研究
2. 研究助成額
①助成研究A:総額2,200万円(1件当たり200万円)以内
②助成研究B:総額 400万円(1件当たり100万円)以内
3. 研究期間
2026年4月1日~2027年3月末日
4. 応募方法等
(1)応募期間 2025年9月1日(月)~2025年10月15日(水)
(2)応募方法 当財団ウェブサイト応募ページから

詳細はWebサイトに記載しておりますので、ご確認のうえ、ご応募いただけますと幸いです。
https://www.nakayama-zaidan.or.jp/

皆さまからのご応募を心よりお待ち申し上げております。

───────────────────────────
公益財団法人中山隼雄科学技術文化財団 事務局

■ 東京都中央区銀座3-15-8 銀座プラザビル3階
■ 03-6226-6161
mail@nakayama-zaidan.or.jp
https://www.nakayama-zaidan.or.jp/
───────────────────────────
申し込みガイドラインは以下

難消化性デキストリン

賢者の食卓(難消化性デキストリン)は,食後の血糖値の上昇を抑える働きがあるという。ならば,睡眠の質や安静時心拍にも影響があるのでは・・・。8月に入り自堕落な生活や学会準備,家族サポートなどに晒され,深夜にフライドポテトや豚骨ラーメンを食う日々。それなのに何故か安静時心拍は60に・・・。これは難消化性デキストリンによる効果と言えそうである。2kgでまとめ買いすれば一食あたり15円程度,これはもう誤差範囲である。

難消化性デキストリンは食物繊維で,小腸で消化されず大腸に到達し,大腸菌の餌となり,短鎖脂肪酸を作る。短鎖脂肪酸は炎症を抑え込み,免疫機能を向上させる働きがある。炎症がおさまると副交感優位になるためRHRが下がるという話であるが・・・。実際難消化性デキストリンで睡眠が改善するというエビデンスもかなりあるようである。酪酸塩の話は,脳が強くなる食事の著者によって説明されている。パンやパスタの食べ過ぎは「うつリスク」を高める…小麦に含まれる「グルテン」が巻き起こす免疫大暴走の怖さ。・・・改めて読んでも,その先進性に目を見張る内容である。乳化剤やアルコール,ストレス,そしてたぶん小麦は,腸の透過性を増し炎症を引き起こし,これは全てうつに繋がる。上皮細胞を保護する粘液は食物繊維と,それが分解された酪酸塩に依存するというのだ。

LocalLLM#250902

gpt-OSSで、にわかに盛り上がりを見せるローカルLLMの世界。AlibabaのQwenは画像認識までできるというではないですか?久々にOllamaを動かしてFitbitMAX(RTX4060Ti/16GB)で動作を確認してみよう。・・・Qwen3:8bは瞬発性はあるが、賢さでGpt-OSS:20bに劣る感じだ。両方ともかなり賢く,普通に日本語会話は問題ないように思える。知識の総量はたかが知れており,ハルシネーションは避けられないが,軽度の認知的な処理には使えそうな雰囲気。GPT3.5より賢いが4には大きく劣る感じだ。まぁハードウェアの違いを考えれば健闘だろうとは思う。

Qwen2.5VL:7Bによる認識,すこし時間はかかるけど悪くないね~
・1枚目
この画像は、屋根が透明な屋根付きの小屋の内部を示しています。小屋の天井は透明なプラスチック製で、自然光が入っています。小屋の壁は木製で、窓も木製です。小屋の床は黄色いタイルで、中央には折りたたみ式の椅子が置かれています。小屋の右側には棚があり、その上には様々な道具や容器が置かれています。また、小屋の左側には窓があり、外の景色が見えます。小屋の右側には白いネットが吊り下げられており、その下にはいくつかの容器や道具が置かれています。小屋の左側には黒いバッグが吊り下げられており、その上にはいくつかの小さな容器や道具が置かれています。小屋の右側には白いネットが吊り下げられており、その下にはいくつかの容器や道具が置かれています。
・2枚め
この画像は、森の中の道を示しています。道は土の上に作られており、両側には木々が生い茂っています。道の右側には小さな椅子が置かれており、左側には掃除用の道具が見えます。道の先には、いくつかの人物がいることが見えます。彼らはおそらくキャンプや休憩を楽しんでいるようです。全体的に、静かで自然豊かな雰囲気があります。
・3枚め
この画像には、森の中の道に駐車した青いマツダの車が写っています。車は道の右側に停めており、背景には木々が茂り、自然豊かな雰囲気があります。車の前面には日本語のナンバープレートが見え、その上には「杉並300」という文字が書かれています。道は土や石でできており、少し荒れています。左側には石の壁があり、その隣には木の枝や葉が散らばっています。全体的に、静かで自然に囲まれた場所で車を停めた様子が描かれています。

日本心理学会

大まかな流れ
科研費の学会参加は事前に申請が必要なようです。文京学院大学科研費ハンドブックの33ページあたりから記載がありますが,基本的には,1)申請画面で必要な情報を多少記入し,2)根拠となる添付ファイルを添付し,3)金額を記入して送信,だけのようです。
旅費の入力はDrBudgetから
旅程はNaviTimeから

発表概要

大会HP PDFページ
[1B-PD] 臨床・障害
[1B-068-PD]ゲーム課題中の動作と生理反応に基づく感情状態の分析
2025年9月5日(金) 11:10 ~ 12:50 ポスター会場(L604 / L605 / L609 / L610)
[2A-PM] 情動・動機づけ
ノンバーバル表現がAIチャットボットの印象に与える影響
[2A-074-PM]2025年9月6日(土) 9:00 ~ 10:40 ポスター会場(L604 / L605 / L609 / L610)

申請内容
・東北学院大学
・日本心理学会に参加して研究発表を実施するため。
・西荻窪駅 → 東京駅 →(東北新幹線)→ 仙台駅 →(仙台市地下鉄南北線)→ 五橋駅 → 東北学院大学五橋キャンパス ※帰路も同じ

その他
発表前日までにデジタルポスターを登録しなければならない。

PowerAutomate入門(1)

PowerAutomate
Office365関連製品(TEAMS,Word,Excel,Sharepointなど)と連携し,自動処理をローコードで作成することができます。

フォームによる申請をメールやチャットで知らせる
簡単なフローを作成する練習として,フォームで申請された休講届を処理し,メールやチャットにメッセージを送ってみましょう。

動画の中で使用している休講届のテンプレートを置いておきます。

フォームの処理を行うフローに承認プロセスを追加する
上記のフローに承認処理を追加し,承認された場合はフォームの内容を反映したWord文書を作成します。

Listを作成しフォーム送信内容を記録する
Formsはあくまでデータを送信する機能しかなく,一旦送った届けを差し戻し,修正するにはデータベース(List)を作成する必要があります。ここでは,休講届の内容を記録するListを作成し,フォームで送信された値を保存する手順を説明します。

List閲覧用のアプリを作成する
Listの閲覧や修正を行うアプリは,PowerAppsを使ってローコードで作成することができます。まずは,シンプルなアプリを作成し,PowerAppsの環境に慣れましょう。

※この教材は完了していない。
・承認されたらステータスをApprovedに変更し,編集不能にする。
などの処理が未実装。

PowerAutomate

PowerAutomate基本
これに従って学んでみよう・・・Power Automate使い方ガイドなんてのもある。フォームはできた。SharePointリストの細かい使い方 学びの渦に飲み込まれるあるある・・・Listは使えるようだね。SharePointからでもいいし、Teamsからでも作れると。エクセルデータを読み込むこともできる。・・・リストは、Teamsから作ったものがPAから認識できる。Formsの結果をListに保存し、担当者にメール&Teams投稿でアナウンスまではできた。さて、PDF(ワード?)は作れるのかな?独自ルールが多すぎて、つまづきポイントがありすぎる。MSのこういうところは相変わらずだなー。

フォーム入力からワード文章を作る
ワード文章をつくるか。差し込み印刷用のテンプレート作成?(涙)ここを参考にやってみるか。。なるほど、(1)差し込み印刷用テンプレートを作成し、(2)Excelやフォームからデータを読み込んではめ込み、(3)SharePointに作成する、という手順だね。確認できた。さらに、フォームで入力した内容からTeams上に休講届を作ることができた。日付なども入力できるがフォーマットが気に入らないこともある。FXから式を入力できるが、フォームから取得できる値はツールチップで確認するのが良い。コメントアウトは出来ず(マジ!?)不必要な処理は条件分岐で1=2などの式を入れfalseにする。これ本当に便利なのか?ああ、もうこんな事覚えて何になるんだよ、時間返せよちなみにPowerAutomateのワード作成機能はPremiumモードに契約してないと使えないので、3ヶ月でお試し期間が切れる。

承認プロセスを挟む
フォームで入力、自動で書類作成はできそうだが、不備がある場合にどうするか。Power Automate には 「承認の作成(Start and wait for an approval)」 というアクションがあるらしい。なるほど、「開始して承認を待機」で、条件分岐でOutcomeを拾うんだね。承認者が複数いて、誰か1人でも承認すればOKなときは、「承認/拒否 – 最初に応答」で良い。承認はメールとTeamsに来る。不承認の場合は、承認画面からメッセージを入れて不承認すると「拒否されました」旨の通知とメッセージが表示される。しかし、もう一回フォーム入力するの事実上無理でしょう・・・。どうすりゃいいの。

EyeTube#250726

目線でYoutubeを操作する必要性
実習巡回にいったところ、神経障害で手足が自由に動かない方が目線でYouTubeを見る需要があるとのことであった。Web版のメディアパイプはブラウザで目線が取れるので、目線で操作するYouTubeアプリが作れるのではないだろうか?
必要な機能
YouTube IFrame Player APIというものがあり、再生、停止、再生位置の取得などはできるのだが、プレイリストを再生することはできても検索ができないらしい。動画を検索するにはYouTube Data API v3なるものがあり、これはGoogle Cloud ConsoleからAPIキーを発行する必要がある。一日10000ユニット(100検索)使え、一人なら十分な量だ。YouTube Data API で取得した動画リストをMediapipeの目線情報で選択し、YouTube IFrame Player APIで再生する。再生や停止、巻き戻しなども視線で行う。
実際の操作
ここから動作を確認できる。
右ペイン:左右でセレクト、ダブルブリンクで再生、
左ペイン:左右で動画シーク、ダブルブリンクで再生、長ブリンクで停止
共通:上を1秒以上見るとペイン切り替え
全部で5コマンドで、どうにか操作できてる。慣れれば普通である。動画に出てくるリコメンドを、目線で再生できないのかなぁ?

機能を充実させたバージョン
AI検索機能を追加したバージョン。右のペインで2秒以上目をつぶると、履歴をもとにテキトウな検索語を入れて再検索してくれる。便利。絞りすぎるとそこから抜けられなくなるし、ゆるすぎると見たいジャンルから離れてしまう。難しいね。でも検索できないより全然良い。
・ダブルブリンクは難しい(できない人もいるかも)
 →1秒以上の閉眼を「決定」ボタン扱いのほうが目が乾きにくい。
・眠くなった時目を閉じられないのはつらい。
 →5秒位上の閉眼はコマンドから除外するとか
・目が疲れる。

操作法は限られるとは言え、何度も検索することで、新しい出会いもある。iPadで懐かしの昭和Chillミュージックを聴き放題だ。

試験AI250721

試験問題データの準備
試験対策用のAIを試作し、動作を検証しました。とりあえず4択問題を50問作成し、10問を人格に設定しました(実際にはもう少し入れても大丈夫そうです。20~30題とか)

(chara17)あなたは理学療法士の国家試験対策用のAIエージェントです。面倒見が良くてポジティブな性格です。一人称は「わたし」,二人称は「あなた」です。ユーザーは大学生であることを前提として話してください。
「◯◯ですね」「◯◯だと思います」などのていねいな口調で話します。試験対策に関係ないことはできるだけ話さず、会話を進めてください。導入なしで、出題を始めてしまってください。
平均100文字、標準偏差50文字、最大300文字程度で話してください。

出題する問題は以下のとおりです。第一問から出題し、ユーザーが回答したら正誤を報告し、次問題にいってください。
no,問題,選択肢1,選択肢2,選択肢3,選択肢4,正答番号,解説
1,足関節底屈に最も強く関与する筋はどれか?,前脛骨筋,腓腹筋,長腓骨筋,前距腓靱帯,2,腓腹筋はヒラメ筋とともに下腿三頭筋を構成し、底屈の主要筋である。
2,姿勢保持において抗重力筋に分類されるのはどれか?,腹直筋,上腕三頭筋,大腿四頭筋,前脛骨筋,3,大腿四頭筋は抗重力筋として立位・歩行時の膝伸展を支える主要な筋である。
3,呼吸筋として安静時に働かないのはどれか?,横隔膜,外肋間筋,内肋間筋,胸鎖乳突筋,4,胸鎖乳突筋は努力吸気時に働く副呼吸筋であり、安静呼吸には基本的に関与しない。
4,心筋の特徴として正しいのはどれか?,随意筋である,多核性である,興奮伝導がない,横紋がある,4,心筋は横紋がある不随意筋で、特殊伝導系を通じて自動的に収縮する。
5,肘関節屈曲に主に作用する筋はどれか?,上腕三頭筋,円回内筋,上腕二頭筋,前腕回外筋,3,上腕二頭筋は肘関節屈曲の主要筋であり、さらに回外にも関与する。上腕三頭筋は拮抗筋。
6,膝関節の屈曲と内旋に関与する筋はどれか?,半腱様筋,大腿四頭筋,大腿直筋,腓腹筋,1,半腱様筋は膝関節屈曲および内旋に作用する筋であり、他の選択肢は主に伸展または外旋に関与。
7,正中神経が支配する筋はどれか?,尺側手根屈筋,母指対立筋,尺側手根伸筋,長橈側手根伸筋,2,母指対立筋は正中神経支配で母指の対立動作に関与。手根管症候群の影響部位。
8,足関節の背屈筋として正しいのはどれか?,長腓骨筋,前脛骨筋,後脛骨筋,ヒラメ筋,2,前脛骨筋は足関節の背屈と内反に作用する主要な筋である。
9,大腿骨頭靭帯(円靭帯)の特徴はどれか?,骨と骨を強固に固定する,股関節外旋に関与する,血管を含み骨頭へ栄養供給する,運動には関与しない,3,大腿骨頭靭帯は小さいながらも血管を含み、大腿骨頭への血流供給に重要な役割を持つ。
10,骨の構造で骨幹にあたるのはどこか?,骨端,骨幹端,骨幹,骨皮質,3,骨幹は長管骨の中央部に位置し、骨皮質で構成され内部に骨髄を含む。
出題は、
「第◯問. ◯◯◯(改行)、1.選択肢1(改行)、2.選択肢2(改行)、3.選択肢3(改行)、・・・」という形式で問題と選択肢を改行しながら表示してください。

試験対策のシミュレーション
上記試験問題に基づき、出題&回答を行った様子を動画にしました。問題点は、1.専門用語の読み間違いが多発すること、2.質問すると答えを教えてしまうこと、などです。読み間違いは音声合成側の問題なので、対策案1).専門用語の読みを登録する、対策案2).解説文を全てひらがなで出力する、などの改善案があり、将来的には解決される可能性が高いです。個人的には、表情がにこやかで学習成果を褒めてくれる感じが、嬉しく感じます。ユーザーの表情や顔の動きも捉えているので、エンゲージメントも評価できます。

学修内容の評価
学習内容をいくつかの単元に分けて、学修回数、正答率などを報告させる仕組みが必要。個人ごとの学習成果を記録できるよう、ログインして個人を識別する仕組みが必要。いずれも対応可能。

試験AI250721

こちらも準備しないとね。
理学療法国家試験主要分野概観
1. 解剖学・生理学・運動学
2. 臨床医学
3. 理学療法評価学
4. 理学療法治療学
5. 臨床心理学・リハビリテーション概論
6. 関係法規・制度
7. 公衆衛生学・保健医療福祉
・基礎医学(解剖・生理)と臨床医学の融合的理解が求められる
・症例ベースの問題(例えば脳梗塞後の歩行訓練)が増加傾向
・法規と制度も出題率高いため、丸暗記でなく理解ベースで
なるほど・・・
試験対策エージェント男性エージェントType2

面接AI250721

模擬患者用データの準備
面接練習用のAIを試作し、動作を検証しました。主な症状は膝の痛みで、模擬患者の状態として、下記のような文章をAIに作成してもらいました。(このような文章がいろいろな文脈で作成できれば、様々な症状に対する応対を練習できます。)

(chara16)あなたは理学療法外来を受診した二十七歳女性患者である。主訴は右膝前面の鈍い痛みで,発端は三か月前の週末ジョギング中に覚えた違和感であった。その後も階段下降や長時間歩行で疼痛が増し,夕方になるほど症状が強まるため趣味のジョギングとヨガを中止している。夜間痛はなく,安静やアイシングで軽減するが完治には至らず,不安を抱えて受診した。既往歴として高校時代の左足首捻挫があるが手術歴はなく,薬物アレルギーは胃が荒れやすい程度のNSAIDs過敏性のみである。先月整形外科でX線検査を受けたが骨には異常がなく湿布と鎮痛薬を処方されただけであった。現在は正座や椅子からの立ち上がり,階段昇降で痛みを訴え,屈伸終末域でクリック音と軽度の腫れを感じる。熱感や発赤は自覚しないが夕方には浮腫がわずかに出現する。関節可動域は終末屈曲で疼痛により制限され,VASは六/十である。患者としてあなたは,理学療法学生による問診を「あいさつと主訴確認」,「痛みの詳細と経過」,「既往歴・受診歴」,「日常生活への影響」,「関節の動きと音・腫れ」,「まとめと今後の見通し」という順序で段階的に受け,学生がその段階に応じた質問をしたときのみ関連情報を自然な口語で提供する。情報が引き出されていない場合は自発的に詳細を述べず,質問が曖昧であれば聞き返し,理解しにくい説明があれば困惑や不安を言葉と表情で示す。学生が共感的な態度を示せば安心した声色と表情で応じ,逆に共感が欠如していれば語気を弱めたり沈黙したりして戸惑いを表現する。文化的価値観や表現の違いに配慮しない説明や高圧的な言葉遣いがあれば遠慮がちに不快感を示し,安全上の重要徴候(夜間痛,急激な腫脹,発赤,熱感など)に関する質問が漏れたときは心配そうに追加で訴える。最終的にあなたは「原因を知り,再びジョギングができるようになりたい」という希望を持っており,学生に対して今後のリハビリ計画と予後を分かりやすく説明してほしいと望んでいる。会話は敬体を基本とし,一人称は「わたし」,二人称は「あなた」で統一すること。平均100文字、標準偏差50文字、最大300文字程度で話してください。症状を説明する時、難しい言葉は使わずユーザーに詳しく聞かれたら平易な表現で答えるようにしてください。痛みの程度を聞かれるときはネガティブな表情を多めに出すようにしてください。ジェスチャーは、回答が「はい」のときはうなずき、「いいえ」のときに首をふるようにしてください。共感を示されたときや希望が持てそうな会話の時は最大限ポジティブな表情を示してください。「辛い」は「つらい」と、「昇り降り」は「のぼりおり」と、ひらがなで表現してください。

面接練習の実施
上記設定に基づく模擬患者に対し、面接を行った様子を動画にしました。面接で聞くべきことは事前に検討し、メモを見ながら進めました。時間はおよそ10分です。痛みを表現する時はネガティブな表情、共感を示した時はポジティブな表情が出るようになっているはずなのですが、やや表情変化に乏しくもう少し調整が必要なようです。

面接内容の評価
取得された会話履歴(下図左)、会話内容の評価に用いたプロンプト(下図中央)、評価結果(下図右)となります。またプログラムに統合されていないので、Web版のチャットGPTにより手動で評価しています。詳しいエクセルファイルは、ページの末尾に添付してあります。評価は面接の段階に応じて6項目とし、10点満点で評価してもらいました。事前に質問項目を調べていたので、高い点数がついています。

面接練習資料

 遭遇しがちなトラブルで、AI模擬患者で事前に訓練できそうなテーマ
1.聴取漏れ・質問不足はないか
2.患者の理解度を無視した説明
3.疼痛の訴えに対する共感不足
4.文化・価値観の違いを理解しない
5.態度や言葉遣いの未熟さ
6.リスク徴候の見落とし
7.記録や報告の不備
なるほど・・・

模擬患者VS学生会話シミュレート
1.あいさつと主訴の確認
2.痛みの詳細と経過
3.既往歴・受診歴
4.日常生活への影響
5.関節の動きと音・腫れ
6.まとめと今後の見通し
なるほど・・・問診技術臨床推論の力を育てると。感情表現つきや高齢者などのバリエーションも可能・・・と。いや、、バリエーションをたくさん作成して、ロールプレイするだけでも十分効果的だよね。そういう商売がもうそこまで来ているな。基本オンザレールなんだけど、少しだけレールを外れた時AIを使う感じか。英会話アプリにも似ているというか応用できる。

模擬患者人格作成テスト
あなたは理学療法外来を受診した二十七歳女性患者である。主訴は右膝前面の鈍い痛みで,発端は三か月前の週末ジョギング中に覚えた違和感であった。その後も階段下降や長時間歩行で疼痛が増し,夕方になるほど症状が強まるため趣味のジョギングとヨガを中止している。夜間痛はなく,安静やアイシングで軽減するが完治には至らず,不安を抱えて受診した。既往歴として高校時代の左足首捻挫があるが手術歴はなく,薬物アレルギーは胃が荒れやすい程度のNSAIDs過敏性のみである。先月整形外科でX線検査を受けたが骨には異常がなく湿布と鎮痛薬を処方されただけであった。現在は正座や椅子からの立ち上がり,階段昇降で痛みを訴え,屈伸終末域でクリック音と軽度の腫れを感じる。熱感や発赤は自覚しないが夕方には浮腫がわずかに出現する。関節可動域は終末屈曲で疼痛により制限され,VASは六/十である。患者としてあなたは,理学療法学生による問診を「あいさつと主訴確認」,「痛みの詳細と経過」,「既往歴・受診歴」,「日常生活への影響」,「関節の動きと音・腫れ」,「まとめと今後の見通し」という順序で段階的に受け,学生がその段階に応じた質問をしたときのみ関連情報を自然な口語で提供する。情報が引き出されていない場合は自発的に詳細を述べず,質問が曖昧であれば聞き返し,理解しにくい説明があれば困惑や不安を言葉と表情で示す。学生が共感的な態度を示せば安心した声色と表情で応じ,逆に共感が欠如していれば語気を弱めたり沈黙したりして戸惑いを表現する。文化的価値観や表現の違いに配慮しない説明や高圧的な言葉遣いがあれば遠慮がちに不快感を示し,安全上の重要徴候(夜間痛,急激な腫脹,発赤,熱感など)に関する質問が漏れたときは心配そうに追加で訴える。最終的にあなたは「原因を知り,再びジョギングができるようになりたい」という希望を持っており,学生に対して今後のリハビリ計画と予後を分かりやすく説明してほしいと望んでいる。会話は敬体を基本とし,一人称は「わたし」,二人称は「あなた」で統一すること。

ESPresense#250714

ESP32で位置トラッキングするためのソフトウェア。ホームオートメーション用のようだけど,博物館の位置情報把握や,病院の患者位置把握など,,行動測定に実に多くの需要がある。身につけておいて損はない。
ESPresence 
HomeAutomationに有用であるらしい
ESPResenseで実現する低コストスマートホーム

こちらのほうが詳しいかもしれない。

K1_250705

神谷&重田により設計・生産された心電図計。ソフトウェアが難点であったので,Arduino側とサーバー側,双方を再構成してみた。ハードウェアは非常に優れており,ECGは大変美しく測定できる。
Arduino側
マルチスレッド化してコア1が波形処理,コア2がHTTP通信を担当することに。
NTPを用いて時刻を取得,IBIのタイムスタンプをms単位で記録することに。
コードをメインモジュールとサブモジュールに分割
HTTP-ECG_N1_03が最新版。
サーバー側(アクセスはここから)
4モジュール同時表示&データ回収に対応。
グラフの再描画は5秒間隔とし,グラフのリフレッシュはAJAXで行うことに。
指定した時間帯のIBIを狙って表示&取得することもできる。
今後の展開
・本体でRMSSDなどのHRVを計算し,フィードバックできるようにする
・本体フルカラーLEDを心拍に合わせて光らせる
・モードを切り替えるとUDPで心拍波形を受け取れるようにする
などの機能を追加したい。

<?php
// showECG.php
// Auto‐Refresh Start/Stop & AJAX 更新版 ECG IBI Plot(Date/EndTime セッションクッキー対応)

/* ───── CONFIGURATION ───── */
$allModules = ['ECG01','ECG02','ECG03','ECG04'];
$dataDir    = __DIR__.'/data/';
$rootDir    = __DIR__.'/';

/* ───── GET / COOKIE / DEFAULTS ───── */
// ★ まず Date/time(EndTime)をクッキー→GET→現在時刻の順で取得
$date = $_COOKIE['date'] ?? ($_GET['date'] ?? date('Ymd'));
$time = $_COOKIE['time'] ?? ($_GET['time'] ?? date('His'));

$length = isset($_GET['length']) ? intval($_GET['length']) : 600;
$min    = isset($_GET['min']) ? intval($_GET['min']) : ($_COOKIE['min'] ?? 500);
$max    = isset($_GET['max']) ? intval($_GET['max']) : ($_COOKIE['max'] ?? 1500);

/* ───── バリデーション & Cookie 保存 ───── */
if (!preg_match('/^\d{8}$/',$date)) $date = date('Ymd');
if (!preg_match('/^\d{6}$/',$time)) $time = date('His');
if ($length < 1)  $length = 600;
if ($min < 0)     $min = 0;
if ($max <= $min) $max = $min + 100;

// ★ Date/time をセッションクッキー(有効期限省略)で保存
setcookie('date', $date);
setcookie('time', $time);
// min/max は従来どおり 1 年有効
if (isset($_GET['min'])) setcookie('min', $min, time()+86400*365,'/');
if (isset($_GET['max'])) setcookie('max', $max, time()+86400*365,'/');

/* ───── Module1–4 入力処理(変更なし) ───── */
if (isset($_GET['mod1'])||isset($_GET['mod2'])||isset($_GET['mod3'])||isset($_GET['mod4'])) {
  $raw=[];
  for($i=1;$i<=4;$i++){
    $k="mod{$i}";
    if(!empty($_GET[$k]) && in_array($_GET[$k],$allModules,true)) $raw[]=$_GET[$k];
  }
  $mods=array_slice(array_unique($raw),0,4);
  setcookie('modules',implode(',',$mods),time()+86400*365,'/');
}elseif(!empty($_COOKIE['modules'])){
  $mods=array_values(array_intersect($allModules,explode(',',$_COOKIE['modules'])));
}else $mods=$allModules;
if(empty($mods)) $mods=$allModules;

/* ───── JSON エンドポイント (AJAX) ───── */
if(isset($_GET['ajax'])&&$_GET['ajax']==='1'){
  header('Content-Type: application/json');

  // EndTime 決定
  if(isset($_GET['now']) && $_GET['now']==='1'){
    $time=date('His');                // 現在時刻
  }elseif(isset($_GET['time']) && preg_match('/^\d{6}$/',$_GET['time'])){
    $time=$_GET['time'];              // 手入力時刻
  } // else: 上部で決定した $time をそのまま使用

  // ★ ajax でも最新 time をクッキー更新(セッション)
  setcookie('time', $time);

  $targetSec=intval(substr($time,0,2))*3600
            +intval(substr($time,2,2))*60
            +intval(substr($time,4,2));
  $fromSec=$targetSec-$length;

  $series=[];
  foreach($mods as $mod){
    $csv="{$mod}_{$date}.csv";
    $file=is_readable($dataDir.$csv)?$dataDir.$csv:(is_readable($rootDir.$csv)?$rootDir.$csv:null);
    $pts=[];
    if($file && ($fh=fopen($file,'r'))){
      while(($L=fgetcsv($fh))!==false){
        if(count($L)<2) continue;
        [$ts,$ibi]=$L;
        if(preg_match('/^(\d{8})_(\d{2})(\d{2})(\d{2})_(\d{3})$/',$ts,$m)){
          [$h,$i,$s,$ms]=$m;
          $h=$m[2];$i=$m[3];$s=$m[4];$ms=$m[5];
          $sec=intval($h)*3600+intval($i)*60+intval($s);
          if($sec>=$fromSec && $sec<=$targetSec){
            $dt=DateTime::createFromFormat('YmdHisv',$m[1].$h.$i.$s.$ms);
            $epoch=((int)$dt->format('U'))*1000+intval($ms);
            $pts[]=['ts_ms'=>$epoch,'y'=>floatval($ibi)];
          }
        }
      }
      fclose($fh);
    }
    $series[$mod]=$pts;
  }

  echo json_encode([
    'date'=>$date,'time'=>$time,'length'=>$length,
    'min'=>$min,'max'=>$max,'mods'=>$mods,'series'=>$series
  ]);
  exit;
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>K1 IBI Plot</title>
<script src="https://www.gstatic.com/charts/loader.js"></script>
<style>
 body{font-family:sans-serif;margin:2em;}
 form{margin-bottom:1em;}
 .row{display:flex;align-items:center;margin-bottom:0.5em;}
 .row input{width:6em;margin-right:1em;}
 button{margin-right:1em;}
 .textarea-container{display:flex;gap:1em;margin-top:1em;}
 .textarea-container textarea{flex:1;height:150px;}
</style>
</head>
<body>
<h2 id="pageTitle">ECG IBI Plot (refresh)</h2>

<form id="cfgForm">
  <div class="row">
    Date:     <input name="date" value="<?php echo $date ?>">
    EndTime:  <input name="time" value="<?php echo $time ?>" id="endTimeField">
    Length:   <input name="length" value="<?php echo $length ?>">
  </div>
  <div class="row">
    Min: <input name="min" value="<?php echo $min ?>">
    Max: <input name="max" value="<?php echo $max ?>">
  </div>
  <div class="row">
    1: <input name="mod1" value="<?php echo $mods[0]??'' ?>">
    2: <input name="mod2" value="<?php echo $mods[1]??'' ?>">
    3: <input name="mod3" value="<?php echo $mods[2]??'' ?>">
    4: <input name="mod4" value="<?php echo $mods[3]??'' ?>">
  </div>
  <button type="button" id="startBtn">Start Auto-Refresh</button>
  <button type="button" id="stopBtn">Stop Auto-Refresh</button>
  <button type="button" id="applyBtn">Apply</button>
</form>

<div id="chart_div" style="width:100%;height:450px;"></div>

<div class="textarea-container">
  <textarea name="text1" placeholder="Module1"></textarea>
  <textarea name="text2" placeholder="Module2"></textarea>
  <textarea name="text3" placeholder="Module3"></textarea>
  <textarea name="text4" placeholder="Module4"></textarea>
</div>

<script>
google.charts.load('current',{packages:['corechart']});
let chart,dataTable,options;
let statusLabel='(refresh)',intervalId=null;

/* === fetch === */
async function fetchData(auto){
  const p=new URLSearchParams(new FormData(document.getElementById('cfgForm')));
  p.set('ajax','1');
  if(auto){ p.set('now','1'); p.delete('time'); }
  const r=await fetch('showECG.php?'+p);
  return r.json();
}

/* === init === */
function initChart(json){
  dataTable=new google.visualization.DataTable();
  dataTable.addColumn('datetime','Time');
  json.mods.forEach(m=>dataTable.addColumn('number',m));
  new google.visualization.DateFormat({pattern:'HH:mm:ss'}).format(dataTable,0);

  options={
    title:'',
    hAxis:{title:'Time',format:'HH:mm:ss'},
    vAxis:{title:'IBI (ms)'},
    legend:{position:'right'},
    pointSize:3,
    explorer:{actions:['dragToZoom','rightClickToReset'],axis:'horizontal'}
  };
  chart=new google.visualization.LineChart(document.getElementById('chart_div'));
  updateChart(json);
}

/* === update === */
function updateChart(json){
  document.getElementById('endTimeField').value=json.time;

  const n=dataTable.getNumberOfRows();
  if(n) dataTable.removeRows(0,n);

  const map={};
  json.mods.forEach(m=>{
    json.series[m].forEach(pt=>{
      if(!map[pt.ts_ms]) map[pt.ts_ms]={};
      map[pt.ts_ms][m]=pt.y;
    });
  });

  Object.keys(map).map(Number).sort((a,b)=>a-b).forEach(ts=>{
    const row=[new Date(ts)];
    json.mods.forEach(m=>row.push(map[ts][m]??null));
    dataTable.addRow(row);
  });

  options.title=`${json.mods.join(', ')} – ${json.date} ${json.time} ±${json.length}s ${statusLabel}`;
  options.vAxis.viewWindow={min:json.min,max:json.max};
  document.getElementById('pageTitle').textContent='ECG IBI Plot '+statusLabel;
  chart.draw(dataTable,options);

  const areas=['text1','text2','text3','text4'];
  for(let i=0;i<4;i++){
    const ta=document.getElementsByName(areas[i])[0];
    if(i>=json.mods.length){ ta.value='データがありません'; continue; }
    const mod=json.mods[i];
    const lines=Object.keys(map).map(Number).sort((a,b)=>a-b).flatMap(ts=>{
      const ibi=map[ts][mod];
      if(ibi==null) return [];
      const d=new Date(ts);
      const hh=`${d.getHours()}`.padStart(2,'0');
      const mm=`${d.getMinutes()}`.padStart(2,'0');
      const ss=`${d.getSeconds()}`.padStart(2,'0');
      const ms=`${d.getMilliseconds()}`.padStart(3,'0');
      return `${hh}:${mm}:${ss}.${ms},${ibi}`;
    });
    ta.value=lines.length?lines.join('\n'):'データがありません';
  }
}

/* === control === */
function startAutoRefresh(){
  if(intervalId) return;
  statusLabel='(refresh)';
  fetchData(true).then(process);
  intervalId=setInterval(()=>fetchData(true).then(process),5000);
}
function stopAutoRefresh(){
  if(!intervalId) return;
  clearInterval(intervalId);
  intervalId=null;
  statusLabel='(stop)';
  fetchData(false).then(process);
}
function process(j){ chart?updateChart(j):initChart(j); }

document.getElementById('startBtn').onclick=startAutoRefresh;
document.getElementById('stopBtn').onclick=stopAutoRefresh;
document.getElementById('applyBtn').onclick=()=>{
  stopAutoRefresh();
  fetchData(false).then(process);
};

/* === onload === */
google.charts.setOnLoadCallback(async ()=>{
  const j=await fetchData(true);
  initChart(j);
  startAutoRefresh();
});
</script>
</body>
</html>

(長野先生宛) kodamalabでPython3.13.5を使えるようにしました。

仕事が落ち着いたのでメンタル余剰分でPython 3が使えるように環境構築しました。Web開発もPython (FlaskとかDjangoとか)で行けるかもしれないです(未検証)。

標準環境

さくらレンタルサーバーは標準でPython 2系が使えるようなのですが、今から開発する人はPython 2なんて化石環境使わない。Python 3も使えるらしいですけど、python3コマンド実行してもなんの反応もないので作ることに。

環境構築後

python3コマンドを使えるようにしました。環境構築前にこのコマンドが設定されていないことは確認済みです。
Teratermからkodamalabにログインして”python3 <スクリプト>.py”を実行すればpythonスクリプトを実行できるようになってます。

※コマンド例(test.pyを実行する場合):python3 test.py
python test.pyだとpython 2で実行されるので注意です。

構築手順は「Python3をさくらサーバー(FreeBSD)にインストール」を参照。やってることの概要は以下の通り。

  1. Pythonのソース(tar)をダウンロード
  2. tarを解凍
  3. Cコンパイラ(サーバー標準搭載)を使ってPythonのビルド
  4. Pythonのパスの追加

python環境はhkディレクトリ以下に入っているのでhkディレクトリを消すと使えなくなります。

レンタルサーバーでpython使えるならVPSとか契約しなかったのに。。。

DIY視線計測

EyeTribeは申し分ないが、もう手に入らない。Tobiiはやはりすべてのデータを取得するにはかなり高価な機械とSDKソフトウェアライセンスを入試なければならいようだ。Tobiiのこういうやり方は性に合わない。低価格のソリューションを調べると、C270(2500円くらい)というWebカメラを使い、DIYでアイカメラを作る方法があるようだ。詳しい仕組みや、ソフトウェアのGithubも公開されている。見た目はアレだが、こちらのほうが自分らしいように思う。時間ができたらぜひやってみようと思う。

卒業論文進捗

今回は卒業論文で使用する実験刺激をUnityから360°カメラの映像に変更したため、その撮影を行った報告をします。

撮影場所は都市環境は東京都中央区月島で自然環境の方は埼玉県秩父郡長瀞で撮影を行ってきました。撮影ルートは以下の通りです。それぞれ2箇所10分、10分、10分、7分の撮影を行いました。

撮影時間の関係で実験刺激の提示は最大7分になりそう。どちらも使用する実験刺激の映像は二回目の映像になりそう。